{*************************************************************************
                Copyright (c) PilotLogic Software House

  Package pl_ORCA
  This file is part of CodeTyphon Studio (https://www.pilotlogic.com/)

 ***** BEGIN LICENSE BLOCK *****
 * Version: LGPL-2.1 with link exception (Modified LGPL)
 *
 * The contents of this file are subject to the 
 * GNU LIBRARY GENERAL PUBLIC LICENSE Version 2.1 (the "License")
 * with the following modification:
 * 
 * As a special exception, the copyright holders of this library give you
 * permission to link this library with independent modules to produce an
 * executable, regardless of the license terms of these independent modules,
 * and to copy and distribute the resulting executable under terms of your choice,
 * provided that you also meet, for each linked independent module, the terms
 * and conditions of the license of that module. An independent module is a
 * module which is not derived from or based on this library. If you modify this
 * library, you may extend this exception to your version of the library, but
 * you are not obligated to do so. If you do not wish to do so, delete this
 * exception statement from your version.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * ***** END LICENSE BLOCK *****
 
***************************************************************************}

Unit orca_editor_bitmap;

{$mode objfpc}{$H+}

Interface

Uses
   LCLProc, LCLIntf, LCLType, LMessages, LResources, LazUtf8,
   Math, typinfo, IntfGraphics,
   Classes, Variants, SysUtils, Contnrs, Forms, Controls, Dialogs, Graphics,
   StdCtrls, DB, DBCtrls, ExtCtrls, Menus, Clipbrd, ActnList, ImgList,
   orca_scene2d;

Type

   TD2BitmapEditor = Class(TD2CustomBitmapEditor)
      d2Scene1: TD2Scene;
      Root1: TD2Background;
      btnLoad: TD2Button;
      btnCancel: TD2Button;
      btnClear: TD2Button;
      btnOkRes: TD2Button;
      btnCancelRes: TD2Button;
      Layout1: TD2Layout;
      Layout2: TD2Layout;
      cnTextWidth: TD2Label;
      cnTextHeight: TD2Label;
      btnOk: TD2Button;
      ScrollBox1: TD2ScrollBox;
      Rectangle1: TD2Panel;
      Preview: TD2PaintBox;
      labelScale: TD2Label;
      trackScale: TD2Track;
      btnCrop: TD2Button;
      Image1: TD2Image;
      btnPaste: TD2Button;
      btnFit: TD2Button;
      btnOriginal: TD2Button;
      editControl: TD2Control;
      GroupBox1: TD2GroupBox;
      cnNewWidth: TD2NumberBox;
      cnNewHeight: TD2NumberBox;
      resizeLayout: TD2Background;
      ShadowEffect1: TD2ShadowEffect;
      btnResize: TD2Button;
      btnSave: TD2Button;
      SaveDialog1: TSaveDialog;
      Procedure btnLoadClick(Sender: TObject);
      Procedure btnCancelClick(Sender: TObject);
      Procedure btnClearClick(Sender: TObject);
      Procedure btnOkResClick(Sender: TObject);
      Procedure btnCancelResClick(Sender: TObject);
      Procedure PreviewPaint(Sender: TObject; aCanvas: TD2Canvas);
      Procedure trackScaleChange(Sender: TObject);
      Procedure btnCropClick(Sender: TObject);
      Procedure btnOkClick(Sender: TObject);
      Procedure trackScaleMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
      Procedure btnPasteClick(Sender: TObject);
      Procedure btnFitClick(Sender: TObject);
      Procedure btnResizeClick(Sender: TObject);
      Procedure cnNewWidthChange(Sender: TObject);
      Procedure cnNewHeightChange(Sender: TObject);
      Procedure btnSaveClick(Sender: TObject);
      Procedure FormCreate(Sender: TObject);
      Procedure FormDestroy(Sender: TObject);
      Procedure btnOriginalClick(Sender: TObject);
   Private
      FBitmap: TD2Bitmap;
      FSourceRect: TD2Rect;
      FCropRect: TD2Selection;
      FOldScale: Single;
      FFileName: String;
   Public
      Procedure AssignFromBitmap(B: TD2Bitmap); Override;
      Procedure AssignToBitmap(B: TD2Bitmap); Override;
      Property FileName: String read FFileName write FFileName;
   End;

Function fnCreateABitmapEditor(Sender: TObject): TD2BitmapEditor;

Var
   GvarD2BitmapEditor: TD2BitmapEditor;

Implementation

{$R *.lfm}

//===================================================================
Function fnCreateABitmapEditor(Sender: TObject): TD2BitmapEditor;
Var
   xDlg: TD2BitmapEditor;
Begin
   xDlg := TD2BitmapEditor.Create(nil);
   Result := xDlg;
End;

//=================== TD2BitmapEditor ===============================

Procedure TD2BitmapEditor.FormCreate(Sender: TObject);
Begin
   resizeLayout.Visible := False;
   FBitmap := TD2Bitmap.Create(1, 1);
   FOldScale := 1;
End;

Procedure TD2BitmapEditor.FormDestroy(Sender: TObject);
Begin
   FreeAndNil(FBitmap);
End;

Procedure TD2BitmapEditor.PreviewPaint(Sender: TObject; aCanvas: TD2Canvas);
Begin
   aCanvas.DrawBitmap(FBitmap, FSourceRect, d2Rect(0, 0, d2RectWidth(FSourceRect) * trackScale.Value, d2RectHeight(FSourceRect) * trackScale.Value), 1);
End;

Procedure TD2BitmapEditor.btnCropClick(Sender: TObject);
Begin
   If FCropRect = nil Then
   Begin
      btnFitClick(Self);

      FCropRect := TD2Selection.Create(Self);
      FCropRect.Parent := Preview;
      btnCrop.TextW := 'Full';
      FCropRect.SetBounds(Trunc(FBitmap.Width * 0.1 * trackScale.Value), Trunc(FBitmap.Height * 0.1 * trackScale.Value),
         (FBitmap.Width * 0.8 * trackScale.Value), Trunc(FBitmap.Height * 0.8 * trackScale.Value));
   End Else Begin
      FreeAndNil(FCropRect);
      Preview.Repaint;
      btnCrop.TextW := 'Crop';
   End;
End;

Procedure TD2BitmapEditor.btnOkClick(Sender: TObject);
Begin
   ModalResult := mrOk;
End;

Procedure TD2BitmapEditor.btnCancelClick(Sender: TObject);
Begin
   ModalResult := mrCancel;
End;

Procedure TD2BitmapEditor.trackScaleMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
Begin
   If ssDouble In Shift Then
      trackScale.Value := 1;
End;


Procedure TD2BitmapEditor.btnOriginalClick(Sender: TObject);
Begin
   trackScale.Value := 1;
End;

Procedure TD2BitmapEditor.btnFitClick(Sender: TObject);
Var
   R: TD2Rect;
Begin
   R := d2Rect(0, 0, FBitmap.Width, FBitmap.Height);
   trackScale.Value := 1 / d2FitRect(R, ScrollBox1.LocalRect);
End;

Procedure TD2BitmapEditor.btnClearClick(Sender: TObject);
Begin
   editControl.Enabled := False;
   btnOk.Enabled := True;
   If FCropRect <> nil Then
      btnCropClick(Self);

   trackScale.Value := 1;
   FBitmap.SetSize(1, 1);
   FSourceRect := d2Rect(0, 0, FBitmap.Width, FBitmap.Height);
   Preview.Width := FBitmap.Width * trackScale.Value;
   Preview.Height := FBitmap.Height * trackScale.Value;
   ScrollBox1.Realign;
End;

Procedure TD2BitmapEditor.btnPasteClick(Sender: TObject);
Var
   B: TBitmap;
Begin
   B := TBitmap.Create;
   B.PixelFormat := pf32bit;
   B.HandleType := bmDIB;
   B.Assign(Clipboard);
   FBitmap.Assign(B);
   B.Free;
End;

Procedure TD2BitmapEditor.btnOkResClick(Sender: TObject);
Var
   tmp: TD2Bitmap;
   SaveFileName: String;
Begin
   SaveFileName := FileName;
   tmp := TD2Bitmap.Create(trunc(cnNewWidth.Value), trunc(cnNewHeight.Value));
   tmp.Canvas.DrawBitmap(FBitmap, d2Rect(0, 0, FBitmap.Width, FBitmap.Height), d2Rect(0, 0, tmp.Width, tmp.Height), 1);
   AssignFromBitmap(tmp);
   tmp.Free;
   FileName := SaveFileName;

   resizeLayout.AnimateFloatWait('Position.Y', -resizeLayout.Height, 0.3);
   resizeLayout.Visible := False;
End;

Procedure TD2BitmapEditor.btnResizeClick(Sender: TObject);
Begin
   cnNewWidth.Value := FBitmap.Width;
   cnNewHeight.Value := FBitmap.Height;

   resizeLayout.Visible := True;
   resizeLayout.Position.Y := -resizeLayout.Height;
   resizeLayout.AnimateFloat('Position.Y', 0, 0.3);
End;

Procedure TD2BitmapEditor.btnCancelResClick(Sender: TObject);
Begin
   resizeLayout.AnimateFloatWait('Position.Y', -resizeLayout.Height, 0.3);
   resizeLayout.Visible := False;
End;

Procedure TD2BitmapEditor.btnSaveClick(Sender: TObject);
Begin
   SaveDialog1.FileName := FFileName;
   If SaveDialog1.Execute Then
      FBitmap.SaveToFile(SaveDialog1.FileName);
End;

Procedure TD2BitmapEditor.cnNewWidthChange(Sender: TObject);
Begin
   cnNewHeight.Value := Round(cnNewWidth.Value * (FBitmap.Height / FBitmap.Width));
End;

Procedure TD2BitmapEditor.cnNewHeightChange(Sender: TObject);
Begin
   cnNewWidth.Value := Round(cnNewHeight.Value * (FBitmap.Width / FBitmap.Height));
End;

Procedure TD2BitmapEditor.AssignToBitmap(B: TD2Bitmap);
Var
   j: Integer;
   CR: TD2Rect;
Begin
   If B <> nil Then
   Begin
      If (FCropRect <> nil) And (FCropRect.Width > 0) And (FCropRect.Height > 0) Then
      Begin
         CR := d2Rect(FCropRect.Position.X / trackScale.Value, FCropRect.Position.Y / trackScale.Value, (FCropRect.Position.X + FCropRect.Width) / trackScale.Value, (FCropRect.Position.Y + FCropRect.Height) / trackScale.Value);
         B.SetSize(Trunc(d2RectWidth(CR)), Trunc(d2RectHeight(CR)));
         For j := 0 To B.Height - 1 Do
            d2MoveLongword(@FBitmap.Scanline[(Trunc(CR.Top) + j)][Trunc(CR.Left)], @B.Scanline[j][0], B.Width);
         B.BitmapChanged;
      End Else Begin
         B.Assign(FBitmap);
      End;
   End;
End;

Procedure TD2BitmapEditor.AssignFromBitmap(B: TD2Bitmap);
Begin
   FileName := '';
   If B <> nil Then
      FBitmap.Assign(B);
   FSourceRect := d2Rect(0, 0, FBitmap.Width, FBitmap.Height);
   Preview.Width := FBitmap.Width * trackScale.Value;
   Preview.Height := FBitmap.Height * trackScale.Value;
   ScrollBox1.Realign;
   If B.Width > 1 Then
   Begin
      editControl.Enabled := True;
      btnOk.Enabled := True;
   End;
End;

Procedure TD2BitmapEditor.btnLoadClick(Sender: TObject);
Var
   D: TOpenDialog;
Begin
   D := TOpenDialog.Create(Application);
   Try
      D.Filter := 'Images|' + GvarD2DefaultFilterClass.GetFileTypes;
      If D.Execute Then
      Begin
         FileName := D.FileName;
         FBitmap.LoadFromFile(D.FileName);
         Preview.Repaint;
         FSourceRect := d2Rect(0, 0, FBitmap.Width, FBitmap.Height);
         Preview.Width := FBitmap.Width * trackScale.Value;
         Preview.Height := FBitmap.Height * trackScale.Value;
         ScrollBox1.Realign;

         editControl.Enabled := True;
         btnOk.Enabled := True;
      End;
   Finally
      D.Free;
   End;
End;

Procedure TD2BitmapEditor.trackScaleChange(Sender: TObject);
Begin
   Preview.Width := FBitmap.Width * trackScale.Value;
   Preview.Height := FBitmap.Height * trackScale.Value;
   ScrollBox1.Realign;

   If FCropRect <> nil Then
   Begin
      FCropRect.Position.X := (FCropRect.Position.X / FOldScale) * trackScale.Value;
      FCropRect.Position.Y := (FCropRect.Position.Y / FOldScale) * trackScale.Value;
      FCropRect.Width := (FCropRect.Width / FOldScale) * trackScale.Value;
      FCropRect.Height := (FCropRect.Height / FOldScale) * trackScale.Value;
   End;

   FOldScale := trackScale.Value;
   labelScale.TextW := 'Scale:' + IntToStr(Round(trackScale.Value * 100)) + '%';
End;

//===========================================

Initialization
   VarCreateABitmapEditor := TD2CreateABitmapEditor(@fnCreateABitmapEditor);
End.
