123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683 |
- unit imageselection;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
- ButtonPanel, fpjson, fgl, LCLTranslator, ComCtrls, Spin, BGRABitmap,
- BGRABitmapTypes, imghelper, tilesetgraphicsunit;
- type
- TImageSelectionType = (istImage, istFace, istSimpleCharset, istFullCharset);
- { TImageSelectionForm }
- TImageSelectionForm = class(TForm)
- DecisionButtonPanel: TButtonPanel;
- FilesListBox: TListBox;
- FilesSplitter: TSplitter;
- HueGroupBox: TGroupBox;
- PreviewPaintBox: TPaintBox;
- PreviewScrollBox: TScrollBox;
- SelectedPanel: TPanel;
- HueTrackBar: TTrackBar;
- HueSpinEdit: TSpinEdit;
- procedure CancelButtonClick(Sender: TObject);
- procedure FilesListBoxSelectionChange(Sender: TObject; User: boolean);
- procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
- procedure HueSpinEditChange(Sender: TObject);
- procedure HueTrackBarChange(Sender: TObject);
- procedure OKButtonClick(Sender: TObject);
- procedure PreviewPaintBoxDblClick(Sender: TObject);
- procedure PreviewPaintBoxMouseUp(Sender: TObject; Button: TMouseButton;
- Shift: TShiftState; X, Y: Integer);
- procedure PreviewPaintBoxPaint(Sender: TObject);
- procedure PreviewScrollBoxResize(Sender: TObject);
- private
- SelectionType: TImageSelectionType;
- Path: String; (* full path, with folder name *)
- HasNone: Boolean;
- HasTilesetItem: Boolean;
- UpdatingHueTrackBar: Boolean;
- ResultWasAccepted: Boolean;
- CurrentImage, RecolouredImage: TBGRABitmap;
- ResizedImage: TBGRACustomBitmap;
- TilesetGraphics: TTilesetGraphics;
- procedure FillFileList;
- procedure UpdatePreview;
- procedure UpdateResizedPreview;
- procedure FillSelectionResults;
- function IsNoneSelected: Boolean;
- function IsTilesetSelected: Boolean;
- function GetCellData(var AWidth, AHeight, PerLine: Integer): Boolean;
- function CountTilesetItems: Integer;
- function GetSelectedTilesetPart: Integer;
- public
- Index, Pattern, Direction, TileId, SelectedTilesetPart: Integer;
- TilesetItemSelected: Boolean;
- SelectedHue: Integer;
- SelectedFilename: String;
- function ShowFaceSelection(AFace: String; AIndex: Integer): Boolean;
- function ShowSimpleCharSelection(AChar: String; AIndex: Integer): Boolean;
- function ShowFullCharSelection(AChar: String; AIndex, APattern, ADir, ATileId: Integer; ATilesetGraphics: TTilesetGraphics): Boolean;
- function ShowSvBattlerSelection(ABattler: String): Boolean;
- function ShowEnemySelection(AEnemy: String; Hue: Integer; IsSv: Boolean): Boolean;
- function ShowTilesetImageSelection(OldFile: String): Boolean;
- end;
- var
- ImageSelectionForm: TImageSelectionForm;
- implementation
- uses Math, globals, gameproject, filehelper;
- {$R *.lfm}
- procedure TImageSelectionForm.CancelButtonClick(Sender: TObject);
- begin
- ResultWasAccepted := False;
- Close;
- end;
- procedure TImageSelectionForm.FilesListBoxSelectionChange(Sender: TObject;
- User: boolean);
- begin
- UpdatePreview;
- Refresh;
- end;
- procedure TImageSelectionForm.FormClose(Sender: TObject;
- var CloseAction: TCloseAction);
- begin
- FillSelectionResults;
- FilesListBox.Items.Clear;
- if ResizedImage <> nil then
- ResizedImage.Free;
- ResizedImage := nil;
- if CurrentImage <> nil then
- CurrentImage.Free;
- CurrentImage := nil;
- end;
- procedure TImageSelectionForm.HueSpinEditChange(Sender: TObject);
- begin
- SelectedHue := HueSpinEdit.Value;
- UpdatingHueTrackBar := True;
- HueTrackBar.Position := SelectedHue;
- UpdatingHueTrackBar := False;
- if CurrentImage = nil then
- Exit;
- if RecolouredImage <> nil then begin
- RecolouredImage.Free;
- RecolouredImage := nil;
- end;
- if (HueSpinEdit.Value <> 0) and (HueSpinEdit.Value <> 360) then
- RecolouredImage := ChangeHue(CurrentImage, HueSpinEdit.Value);
- UpdateResizedPreview;
- Refresh
- end;
- procedure TImageSelectionForm.HueTrackBarChange(Sender: TObject);
- begin
- if not UpdatingHueTrackBar then
- HueSpinEdit.Value := HueTrackBar.Position;
- end;
- procedure TImageSelectionForm.FillSelectionResults;
- begin
- if IsNoneSelected then begin
- TilesetItemSelected := False;
- SelectedFilename := '';
- Index := 0;
- end
- else if IsTilesetSelected then begin
- TilesetItemSelected := True;
- SelectedFilename := '';
- end
- else begin
- TilesetItemSelected := False;
- SelectedFilename := FilesListBox.Items[FilesListBox.ItemIndex];
- if TGameProject.IsDollarName(SelectedFilename) then
- Index := 0;
- end;
- end;
- function TImageSelectionForm.IsNoneSelected: Boolean;
- begin
- if HasNone then
- IsNoneSelected := FilesListBox.ItemIndex = 0
- else
- IsNoneSelected := False;
- end;
- function TImageSelectionForm.IsTilesetSelected: Boolean;
- begin
- if HasTilesetItem then begin
- IsTilesetSelected := FilesListBox.ItemIndex >= FilesListBox.Count - CountTilesetItems; //TODO: preselect the correct tileset
- end
- else
- IsTilesetSelected := False;
- end;
- procedure TImageSelectionForm.OKButtonClick(Sender: TObject);
- begin
- ResultWasAccepted := True;
- Close;
- end;
- procedure TImageSelectionForm.PreviewPaintBoxDblClick(Sender: TObject);
- begin
- (* This causes problems on Windows: if you close the window by double-click,
- subsequent calls to show ImageSelectionForm will not show anything.
- I don't know what causes this, so let's keep it commented out for a while. *)
- {$IF not Defined(LCLWin32)}
- //TODO: check that this doesn't happen in other widgetsets
- ResultWasAccepted := True;
- Close;
- {$ENDIF}
- end;
- procedure TImageSelectionForm.PreviewPaintBoxMouseUp(Sender: TObject;
- Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
- var
- XIndex, YIndex: Integer;
- CellWidth: Integer = 0;
- CellHeight: Integer = 0;
- CellsPerLine: Integer = 0;
- ResizedCellWidth, ResizedCellHeight: Integer;
- begin
- if GetCellData(CellWidth, CellHeight, CellsPerLine) then begin
- //TODO: move this our into a separate routine?
- ResizedCellWidth := CellWidth * ResizedImage.Width div CurrentImage.Width;
- ResizedCellHeight:=CellHeight*ResizedImage.Height div CurrentImage.Height;
- XIndex := X div ResizedCellWidth;
- YIndex := Y div ResizedCellHeight;
- Index := YIndex*CellsPerLine + XIndex;
- Refresh;
- end;
- end;
- procedure TImageSelectionForm.PreviewPaintBoxPaint(Sender: TObject);
- var
- Dst, SelectionRect: TRect;
- CellWidth: Integer = 0;
- CellHeight: Integer = 0;
- CellsPerLine: Integer = 0;
- ResizedCellWidth, ResizedCellHeight: Double;
- begin
- if (ResizedImage <> nil) and (CurrentImage <> nil) then begin
- Dst :=Rect(0, 0, PreviewPaintBox.ClientWidth, PreviewPaintBox.ClientHeight);
- //PreviewPaintBox.Canvas.StretchDraw(Dst, ResizedImage);
- ResizedImage.Draw(PreviewPaintBox.Canvas, Dst, False);
- if GetCellData(CellWidth, CellHeight, CellsPerLine) then begin
- //TODO: draw index
- ResizedCellWidth := CellWidth * ResizedImage.Width / CurrentImage.Width;
- ResizedCellHeight:=CellHeight*ResizedImage.Height / CurrentImage.Height;
- SelectionRect.Left := Floor((Index mod CellsPerLine) * ResizedCellWidth);
- SelectionRect.Top := Floor((Index div CellsPerLine) * ResizedCellHeight);
- SelectionRect.Right := Ceil(SelectionRect.Left + ResizedCellWidth);
- SelectionRect.Bottom := Ceil(SelectionRect.Top + ResizedCellHeight);
- PreviewPaintBox.Canvas.Brush.Style := bsClear;
- PreviewPaintBox.Canvas.Pen.Color := clWhite;
- PreviewPaintBox.Canvas.Pen.Width := 2;
- PreviewPaintBox.Canvas.Rectangle(SelectionRect);
- PreviewPaintBox.Canvas.Pen.Color := clBlack;
- SelectionRect.Inflate(-2, -2, -2, -2);
- PreviewPaintBox.Canvas.Rectangle(SelectionRect);
- PreviewPaintBox.Canvas.Pen.Color := clWhite;
- SelectionRect.Inflate(-2, -2, -2, -2);
- PreviewPaintBox.Canvas.Rectangle(SelectionRect);
- //ResizedCellHeight
- end;
- end;
- end;
- function TImageSelectionForm.GetCellData(var AWidth, AHeight, PerLine: Integer): Boolean;
- begin
- if IsTilesetSelected then begin
- AWidth := 48; //TODO: adapt for other tile sizes
- AHeight := 48; //TODO: adapt for other tile sizes
- PerLine := 8;
- GetCellData := True;
- end
- else if IsNoneSelected then begin
- GetCellData := False;
- end
- else if CurrentImage <> nil then begin
- if SelectionType = istFace then begin
- AWidth := 144;
- AHeight := 144;
- PerLine := 4;
- GetCellData := True;
- end
- else if SelectionType = istSimpleCharset then begin
- if TGameProject.IsDollarName(FilesListBox.Items[FilesListBox.ItemIndex]) then begin
- AWidth := CurrentImage.Width;
- AHeight := CurrentImage.Height;
- PerLine := 1;
- GetCellData := True;
- end
- else begin
- AWidth := CurrentImage.Width div 4;
- AHeight := CurrentImage.Height div 2;
- PerLine := 4;
- GetCellData := True;
- end;
- end
- else if SelectionType = istFullCharset then begin
- if TGameProject.IsDollarName(FilesListBox.Items[FilesListBox.ItemIndex]) then begin
- AWidth := CurrentImage.Width div 3;
- AHeight := CurrentImage.Height div 4;
- PerLine := 3;
- GetCellData := True;
- end
- else begin
- AWidth := CurrentImage.Width div (3*4);
- AHeight := CurrentImage.Height div (4*2);
- PerLine := 3*4;
- GetCellData := True;
- end;
- end
- else begin
- GetCellData := False;
- end;
- end;
- end;
- function TImageSelectionForm.CountTilesetItems: Integer;
- var
- I: Integer;
- begin
- CountTilesetItems := 0;
- if HasTilesetItem and (TilesetGraphics <> nil) then
- for I := 5 to High(TilesetGraphics.Bitmaps) do
- if TilesetGraphics.HasSet(I) then
- Inc(CountTilesetItems);
- end;
- function TImageSelectionForm.GetSelectedTilesetPart: Integer;
- var
- I, StartIndex, StartListItem: Integer;
- begin
- GetSelectedTilesetPart := 0;
- if not HasTilesetItem or (TilesetGraphics = nil) then begin
- Exit;
- end;
- StartIndex := 8;
- StartListItem := FilesListBox.Count -1;
- repeat
- if TilesetGraphics.HasSet(StartIndex) then
- if StartListItem = FilesListBox.ItemIndex then begin
- GetSelectedTilesetPart := StartIndex;
- Exit
- end else
- Dec(StartListItem);
- Dec(StartIndex);
- until StartIndex <= 4;
- end;
- procedure TImageSelectionForm.PreviewScrollBoxResize(Sender: TObject);
- begin
- UpdateResizedPreview;
- end;
- procedure TImageSelectionForm.FillFileList;
- var
- PreselectedIndex: Integer = -1;
- I: Integer;
- FileNames: TFilenameList;
- function GetAvailableTilesetId: Integer;
- var
- CurrentNumber, SetIndex: Integer;
- begin
- Result := 0;
- CurrentNumber := 0;
- for SetIndex := 5 to 8 do
- if TilesetGraphics <> nil then
- if TilesetGraphics.HasSet(SetIndex) then
- if SelectedTilesetPart = SetIndex - 5 then begin
- Result := CurrentNumber;
- Exit;
- end else
- Inc(CurrentNumber);
- end;
- procedure AddTilesetItems;
- begin
- if (TilesetGraphics <> nil) then begin
- if TilesetGraphics.HasSet(5) then
- FilesListBox.Items.Add(TilesBFilename);
- if TilesetGraphics.HasSet(6) then
- FilesListBox.Items.Add(TilesCFilename);
- if TilesetGraphics.HasSet(7) then
- FilesListBox.Items.Add(TilesDFilename);
- if TilesetGraphics.HasSet(8) then
- FilesListBox.Items.Add(TilesEFilename);
- end;
- if TilesetItemSelected then begin
- PreselectedIndex := FilesListBox.Items.Count
- - CountTilesetItems + GetAvailableTilesetId;
- end;
- end;
- begin
- FilesListBox.Items.BeginUpdate;
- FilesListBox.Items.Clear;
- if HasNone then begin
- FilesListBox.Items.Add(NoneFilename);
- if (SelectedFilename = '') and not TilesetItemSelected then begin
- PreselectedIndex := FilesListBox.Items.Count - 1;
- end;
- end;
- //TODO: show files in subfolders???
- //TODO: make sure FilesListBox doesn't mess with our filenames (what about names w/ line feed in them? do we even need this brain damage?)
- FileNames := FillFilenameList(Path);
- for I := 0 to FileNames.Count - 1 do begin
- FilesListBox.Items.Add(FileNames[I]);
- if SelectedFilename = FileNames[I] then begin
- PreselectedIndex := FilesListBox.Items.Count - 1;
- end;
- end;
- FileNames.Free;
- AddTilesetItems;
- FilesListBox.Items.EndUpdate;
- if PreselectedIndex > -1 then
- FilesListBox.ItemIndex := PreselectedIndex;
- end;
- procedure TImageSelectionForm.UpdatePreview;
- var
- Filename: String = '';
- NewImage: TBGRABitmap = nil;
- BitmapToCopy: TBGRABitmap = nil;
- begin
- if CurrentImage <> nil then begin
- CurrentImage.Free;
- CurrentImage := nil;
- end;
- if RecolouredImage <> nil then begin
- RecolouredImage.Free;
- RecolouredImage := nil;
- end;
- if IsNoneSelected then begin
- //TODO: (None) item handling
- end
- else if IsTilesetSelected then begin
- BitmapToCopy := TilesetGraphics.Bitmaps[GetSelectedTilesetPart];
- NewImage := TBGRABitmap.Create(BitmapToCopy.Width div 2, BitmapToCopy.Height * 2, BGRAWhite);
- NewImage.CanvasBGRA.CopyRect(0, 0, BitmapToCopy, Rect(
- 0,
- 0,
- BitmapToCopy.Width div 2,
- BitmapToCopy.Height));
- NewImage.CanvasBGRA.CopyRect(0, BitmapToCopy.Height, BitmapToCopy,
- Rect(
- BitmapToCopy.Width div 2,
- 0,
- BitmapToCopy.Width,
- BitmapToCopy.Height));
- SelectedTilesetPart := GetSelectedTilesetPart - 5;
- CurrentImage := NewImage;
- //TODO: (Tiles) item handling
- end
- else begin
- try
- if (FilesListBox.ItemIndex >= 0) and (FilesListBox.ItemIndex < FilesListBox.Count) then
- Filename := Path + DirectorySeparator
- + FilesListBox.Items[FilesListBox.ItemIndex]
- + '.png';
- if FileExists(Filename) then
- NewImage := TBGRABitmap.Create(Filename);
- except
- if NewImage <> nil then
- NewImage.Free;
- end;
- CurrentImage := NewImage;
- if (CurrentImage <> nil) and (HueSpinEdit.Value <> 0) and (HueSpinEdit.Value <> 360) then
- RecolouredImage := ChangeHue(CurrentImage, HueSpinEdit.Value);
- end;
- UpdateResizedPreview;
- end;
- procedure TImageSelectionForm.UpdateResizedPreview;
- var
- ResizedWidth, ResizedHeight: Integer;
- ImgToResize: TBGRABitmap;
- begin
- if (CurrentImage <> nil) and (PreviewScrollBox.ClientWidth > 20) then begin
- if ResizedImage <> nil then begin
- ResizedImage.Free;
- ResizedImage := nil;
- end;
- if RecolouredImage <> nil then
- ImgToResize := RecolouredImage
- else
- ImgToResize := CurrentImage;
- (* Using PreviewScrollBox.ClientWidth causes an infinite loop here, so
- let's do this hack and hope scrollbar is around 20px. (If it's not,
- a small edge of the image will be hidden, which is acceptable.) *)
- ResizedWidth := PreviewScrollBox.Width - 20;
- ResizedHeight := ImgToResize.Height
- * ResizedWidth
- div Max(ImgToResize.Width, 1);
- PreviewPaintBox.ClientWidth := ResizedWidth;
- PreviewPaintBox.ClientHeight := ResizedHeight;
- (* Left can be changed when flipping for RTL, so ensure it's correct *)
- PreviewPaintBox.Left := 0;
- ResizedImage := ImgToResize.Resample(ResizedWidth, ResizedHeight, rmSimpleStretch);
- end;
- end;
- function TImageSelectionForm.ShowFaceSelection(AFace: String; AIndex: Integer): Boolean;
- begin
- ResultWasAccepted := False;
- SelectionType := istFace;
- SelectedFilename := AFace;
- HasTilesetItem := False;
- TilesetItemSelected := False;
- Index := AIndex;
- Path := Game.GetImageSubfolderPath('faces');
- HueGroupBox.Hide;
- HueSpinEdit.Value := 0;
- HasNone := True;
- FillFileList;
- UpdatePreview;
- ShowModal;
- ShowFaceSelection := ResultWasAccepted;
- end;
- function TImageSelectionForm.ShowSimpleCharSelection(AChar: String; AIndex: Integer): Boolean;
- begin
- ResultWasAccepted := False;
- SelectionType := istSimpleCharset;
- SelectedFilename := AChar;
- TilesetItemSelected := False;
- HasTilesetItem := False;
- Index := AIndex;
- Path := Game.GetImageSubfolderPath('characters');
- HueGroupBox.Hide;
- HueSpinEdit.Value := 0;
- HasNone := True;
- FillFileList;
- UpdatePreview;
- ShowModal;
- ShowSimpleCharSelection := ResultWasAccepted;
- end;
- function TImageSelectionForm.ShowFullCharSelection(AChar: String; AIndex,
- APattern, ADir, ATileId: Integer; ATilesetGraphics: TTilesetGraphics): Boolean;
- var
- CharsPerLine: Integer = 4;
- CharOffset: Integer = 0;
- begin
- ResultWasAccepted := False;
- SelectionType := istFullCharset;
- SelectedFilename := AChar;
- TilesetItemSelected := False;
- if TGameProject.IsDollarName(AChar) then
- CharsPerLine := 1;
- if AIndex > 3 then
- CharOffset := 48 + ((AIndex - 4) * 3)
- else
- CharOffset := (AIndex * 3);
- Index := CharOffset + ((ADir div 2 - 1) * (CharsPerLine * 3)) + APattern;
- Path := Game.GetImageSubfolderPath('characters');
- HueGroupBox.Hide;
- HueSpinEdit.Value := 0;
- HasNone := True;
- HasTilesetItem := True;
- TilesetGraphics := ATilesetGraphics;
- TileId := ATileId;
- SelectedTilesetPart := TileId div 256;
- TilesetItemSelected := SelectedFilename = '';
- if TilesetItemSelected then
- Index := TileId mod 256;
- FillFileList;
- UpdatePreview;
- ShowModal;
- if TilesetItemSelected then begin
- SelectedFilename := '';
- TileId := Index + (SelectedTilesetPart * 256); //TODO: preset SelectedTilesetPart
- end else begin
- if TGameProject.IsDollarName(SelectedFilename) then
- CharsPerLine := 1
- else
- CharsPerLine := 4;
- Direction := (((Index div (CharsPerLine * 3)) mod 4) + 1) * 2;
- Pattern := Index mod 3;
- if Index < 48 then
- Index := (Index mod (CharsPerLine * 3)) div 3
- else
- Index := 4 + (Index mod (CharsPerLine * 3)) div 3
- end;
- ShowFullCharSelection := ResultWasAccepted;
- end;
- function TImageSelectionForm.ShowSvBattlerSelection(ABattler: String): Boolean;
- begin
- ResultWasAccepted := False;
- SelectionType := istImage;
- SelectedFilename := ABattler;
- TilesetItemSelected := False;
- HasTilesetItem := False;
- Path := Game.GetImageSubfolderPath('sv_actors');
- HueGroupBox.Hide;
- HueSpinEdit.Value := 0;
- HasNone := True;
- FillFileList;
- UpdatePreview;
- ShowModal;
- ShowSvBattlerSelection := ResultWasAccepted;
- end;
- function TImageSelectionForm.ShowEnemySelection(AEnemy: String; Hue: Integer;
- IsSv: Boolean): Boolean;
- begin
- ResultWasAccepted := False;
- SelectionType := istImage;
- SelectedFilename := AEnemy;
- TilesetItemSelected := False;
- HasTilesetItem := False;
- if IsSv then
- Path := Game.GetImageSubfolderPath('sv_enemies')
- else
- Path := Game.GetImageSubfolderPath('enemies');
- HueGroupBox.Show;
- HueSpinEdit.Value := Hue;
- HueTrackBar.Position := Hue;
- HasNone := True;
- FillFileList;
- UpdatePreview;
- ShowModal;
- ShowEnemySelection := ResultWasAccepted;
- end;
- function TImageSelectionForm.ShowTilesetImageSelection(OldFile: String
- ): Boolean;
- begin
- ResultWasAccepted := False;
- SelectionType := istImage;
- SelectedFilename := OldFile;
- TilesetItemSelected := False;
- HasTilesetItem := False;
- Path := Game.GetImageSubfolderPath('tilesets');
- HueGroupBox.Hide;
- HasNone := True;
- FillFileList;
- UpdatePreview;
- ShowModal;
- ShowTilesetImageSelection := ResultWasAccepted;
- end;
- end.
|