2011. március 21., hétfő

A TDBImage component that reads and writes *.jpg images


Problem/Question/Abstract:

A TDBImage component that reads and writes *.jpg images

Answer:

unit MyImage;

{ Unit MyImage; Toni Martir, techni-web@pala.com}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DB,
  DBCtrls, Menus, clipbrd, ExtDlgs, extctrls, dbtables, jpeg, noudbct;

type
  TZoomType = 1..1000;

  TDBMyImage = class(TDBImage2)
  private
    { Private declarations }
    PopUpDefault: TPopUpMenu;
    popcortar, poppegar, popabrir: TMenuItem;
    FSaveJpeg, FAutosize: Boolean;
    FZoom: TZoomType;
    FCompressionQuality: TJPEgQualityRange;
    procedure ComprovaEditant;
    procedure Cortar(Sender: TObject);
    procedure Copiar(Sender: TObject);
    procedure Pegar(Sender: TObject);
    procedure Guardar(Sender: TObject);
    procedure QuanPopUp(Sender: TObject);
    procedure SaveAsJPeg;
  protected
    { Protected declarations }
    procedure PictureChanged(Sender: TObject); override;
    procedure loaded; override;
  public
    { Public declarations }
    DefaultWidth: integer;
    DefaultHeight: integer;
    procedure Abrir(Sender: TObject);
    procedure LoadPicture; override;
    constructor Create(AOwner: TComponent); override;
  published
    { Published declarations }
    property SaveJpeg: Boolean read FSaveJpeg write FSaveJPeg default false;
    property CompressionQuality: TJPEgQualityRange read FCompressionQuality
      write FCompressionQuality default 70;
    property Autosize: Boolean read FAutosize write FAutosize default False;
    property Zoom: TZoomType read FZoom write FZoom default 100;
  end;

implementation

var
  cajpeg: array[0..10] of char = (chr($FF), chr($D8), chr($FF), chr($E0), chr($0),
    chr($10), 'J', 'F', 'I', 'F', chr(0));

constructor TDBMyImage.Create(AOwner: TComponent);
var
  item: TMenuItem;
begin
  inherited Create(AOWner);
  FCompressionQuality := 70;
  FSaveJPeg := false;
  FZoom := 100;
  PopUpMenu := TPopUpMenu.Create(Self);
  PopUpDefault := PopUpMenu;
  PopUpMenu.Onpopup := quanpopup;
  item := TMenuItem.Create(Self);
  item.Caption := '&Cortar';
  item.OnClick := Cortar;
  PopUpMenu.Items.Add(item);
  popcortar := item;
  item := TMenuItem.Create(Self);
  item.Caption := 'C&opiar';
  item.OnClick := Copiar;
  PopUpMenu.Items.Add(item);
  item := TMenuItem.Create(Self);
  item.Caption := '&Pegar';
  item.OnClick := Pegar;
  PopUpMenu.Items.Add(item);
  poppegar := item;
  item := TMenuItem.Create(Self);
  item.Caption := '&Abrir...';
  item.OnClick := Abrir;
  PopUpMenu.Items.Add(item);
  popabrir := item;
  item := TMenuItem.Create(Self);
  item.Caption := '&Guardar como...';
  item.OnClick := Guardar;
  PopUpMenu.Items.Add(item);
end;

procedure TDBMyImage.Cortar(Sender: TObject);
begin
  if readonly then
    exit;
  if Picture.Graphic <> nil then
  begin
    ComprovaEditant;
    CopyToClipboard;
    Picture.Graphic := nil;
  end;
end;

procedure TDBMyImage.Copiar(Sender: TObject);
begin
  if Picture.Graphic <> nil then
    CopyToClipboard;
end;

procedure TDBMyImage.Pegar(Sender: TObject);
begin
  if ClipBoard.HasFormat(CF_BITMAP) then
  begin
    ComprovaEditant;
    if not FSaveJpeg then
    begin
      PasteFromClipboard;
    end
    else
    begin
      PasteFromClipboard;
      SaveAsJpeg;
    end;
  end;
end;

procedure TDBMyImage.Abrir(Sender: TObject);
var
  Dia: TOpenPictureDialog;
  Image1: TImage;
begin
  if readonly then
    exit;
  Dia := TOpenPictureDialog.Create(Self);
  try
    Dia.Title := 'Abrir imagen';
    if Dia.Execute then
    begin
      ComprovaEditant;
      Image1 := TImage.Create(Self);
      try
        Image1.Picture.LoadFromFile(Dia.Filename);
        Picture.Bitmap.Assign(Image1.Picture.Graphic);
        if FSaveJpeg then
          SaveAsJPeg;
      finally
        Image1.free;
      end;
    end;
  finally
    Dia.free;
  end;
end;

procedure TDBMyImage.Guardar(Sender: TObject);
var
  Dia: TSavePictureDialog;
begin
  Dia := TSavePictureDialog.Create(Self);
  try
    Dia.Title := 'Guardar imagen';
    if Dia.Execute then
    begin
      Picture.Graphic.SaveToFile(Dia.Filename);
    end;
  finally
    Dia.free;
  end;
end;

procedure TDBMyImage.ComprovaEditant;
var
  Data: TDataSet;
begin
  if Datasource = nil then
    Abort;
  if Length(DataField) = 0 then
    Abort;
  Data := DataSource.DataSet;
  if Data = nil then
    Abort;
  if not (Data.State in dsEditModes) then
    if ((Data.BOF) and (Data.EOF)) then
      Data.Insert
    else
      Data.Edit;
end;

procedure TDBMyimage.LoadPicture;
var
  camp: TBlobField;
  jpeg1: TJpegImage;
  stream: TBlobStream;
  carregat: Boolean;
  buf: array[0..10] of char;
begin
  carregat := false;
  try
    if Field <> nil then
    begin
      if field is TBlobField then
      begin
        camp := TBlobField(Field);
        stream := TBlobStream.Create(camp, bmRead);
        try
          if 11 = stream.Read(buf, 11) then
          begin
            if CompareMem(@cajpeg, @buf, 11) then
            begin
              Stream.Seek(soFromBeginning, 0);
              Jpeg1 := TJpegImage.Create;
              try
                Jpeg1.LoadFromStream(Stream);
                Picture.Assign(Jpeg1);
                carregat := true;
              finally
                jpeg1.free;
              end;
            end;
          end;
        finally
          stream.free;
        end;
      end;
    end;
  except
  end;
  if not carregat then
    inherited LoadPicture;
end;

procedure TDBMyImage.QuanPopUp(Sender: TObject);
begin
  if ReadOnly then
  begin
    popabrir.enabled := false;
    poppegar.enabled := false;
    popcortar.enabled := false;
  end
  else
  begin
    popabrir.enabled := true;
    poppegar.enabled := true;
    popcortar.enabled := true;
  end;
end;

procedure TDBMyimage.SaveAsJPeg;
var
  jpeg1: TJpegImage;
  blobs: TBlobStream;
  camp: TBlobField;
begin
  if readonly then
    exit;
  if Field = nil then
    exit;
  if not (field is TBlobField) then
    exit;
  camp := TBlobfield(field);
  jpeg1 := TJpegImage.Create;
  try
    jpeg1.Assign(picture.graphic);
    jpeg1.CompressionQuality := FCompressionQuality;
    blobs := TBlobStream.Create(camp, bmWrite);
    try
      blobs.Truncate;
      jpeg1.SaveToStream(blobs);
    finally
      blobs.free;
    end;
    blobs := TBlobStream.Create(camp, bmRead);
    try
      jpeg1.LoadFromStream(blobs);
    finally
      blobs.free;
    end;
  finally
    jpeg1.free;
  end;
end;

procedure TDBMyimage.PictureChanged(Sender: TObject);
begin
  if (FAutosize and (Picture <> nil)) then
  begin
    if Picture.Graphic <> nil then
    begin
      if ((Picture.graphic.width = 0) or (Picture.graphic.height = 0)) then
      begin
        width := defaultwidth;
        height := defaultheight;
      end
      else
      begin
        if Stretch then
        begin
          width := MulDiv(Picture.graphic.width, Zoom, 100);
          height := MulDiv(Picture.graphic.height, Zoom, 100);
        end
        else
        begin
          width := Picture.graphic.width;
          height := Picture.graphic.height;
        end;
      end;
    end;
  end;
  inherited PictureChanged(Sender);
end;

procedure TDBMyImage.loaded;
begin
  inherited loaded;
  defaultwidth := width;
  defaultheight := height;
end;

{New unit

Unic canvi per qu� funcioni
Declarar virtual LoadPicture
i procedure PictureChanged(Sender: TObject);virtual}

unit noudbct;

{$R-}

interface

uses
  Windows, SysUtils, Messages, Classes, Controls, Forms, Graphics,
  Menus, StdCtrls, ExtCtrls, Mask, Buttons, ComCtrls, Db, dbctrls;

type

  { TDBImage }

  TDBImage2 = class(TCustomControl)
  private
    FDataLink: TFieldDataLink;
    FPicture: TPicture;
    FBorderStyle: TBorderStyle;
    FAutoDisplay: Boolean;
    FStretch: Boolean;
    FCenter: Boolean;
    FPictureLoaded: Boolean;
    FQuickDraw: Boolean;
    procedure DataChange(Sender: TObject);
    function GetDataField: string;
    function GetDataSource: TDataSource;
    function GetField: TField;
    function GetReadOnly: Boolean;
    procedure SetAutoDisplay(Value: Boolean);
    procedure SetBorderStyle(Value: TBorderStyle);
    procedure SetCenter(Value: Boolean);
    procedure SetDataField(const Value: string);
    procedure SetDataSource(Value: TDataSource);
    procedure SetPicture(Value: TPicture);
    procedure SetReadOnly(Value: Boolean);
    procedure SetStretch(Value: Boolean);
    procedure UpdateData(Sender: TObject);
    procedure CMGetDataLink(var Message: TMessage); message CM_GETDATALINK;
    procedure CMEnter(var Message: TCMEnter); message CM_ENTER;
    procedure CMExit(var Message: TCMExit); message CM_EXIT;
    procedure CMTextChanged(var Message: TMessage); message CM_TEXTCHANGED;
    procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
    procedure WMLButtonDblClk(var Message: TWMLButtonDblClk);
      message WM_LBUTTONDBLCLK;
    procedure WMCut(var Message: TMessage); message WM_CUT;
    procedure WMCopy(var Message: TMessage); message WM_COPY;
    procedure WMPaste(var Message: TMessage); message WM_PASTE;
    procedure WMSize(var Message: TMessage); message WM_SIZE;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    function GetPalette: HPALETTE; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure KeyPress(var Key: Char); override;
    procedure Notification(AComponent: TComponent;
      Operation: TOperation); override;
    procedure Paint; override;
    procedure PictureChanged(Sender: TObject); virtual;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure CopyToClipboard;
    procedure CutToClipboard;
    function ExecuteAction(Action: TBasicAction): Boolean; override;
    procedure LoadPicture; virtual;
    procedure PasteFromClipboard;
    function UpdateAction(Action: TBasicAction): Boolean; override;
    property Field: TField read GetField;
    property Picture: TPicture read FPicture write SetPicture;
  published
    property Align;
    property Anchors;
    property AutoDisplay: Boolean read FAutoDisplay write SetAutoDisplay default True;
    property BorderStyle: TBorderStyle read FBorderStyle write SetBorderStyle default bsSingle;
    property Center: Boolean read FCenter write SetCenter default True;
    property Color;
    property Constraints;
    property Ctl3D;
    property DataField: string read GetDataField write SetDataField;
    property DataSource: TDataSource read GetDataSource write SetDataSource;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property Font;
    property ParentColor default False;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly: Boolean read GetReadOnly write SetReadOnly default False;
    property QuickDraw: Boolean read FQuickDraw write FQuickDraw default True;
    property ShowHint;
    property Stretch: Boolean read FStretch write SetStretch default False;
    property TabOrder;
    property TabStop default True;
    property Visible;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnStartDock;
    property OnStartDrag;
  end;

implementation

uses
  Clipbrd, DBConsts, Dialogs;

{ TDBImage2 }

constructor TDBImage2.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ControlStyle := ControlStyle + [csOpaque, csReplicatable];
  if not NewStyleControls then
    ControlStyle := ControlStyle + [csFramed];
  Width := 105;
  Height := 105;
  TabStop := True;
  ParentColor := False;
  FPicture := TPicture.Create;
  FPicture.OnChange := PictureChanged;
  FBorderStyle := bsSingle;
  FAutoDisplay := True;
  FCenter := True;
  FDataLink := TFieldDataLink.Create;
  FDataLink.Control := Self;
  FDataLink.OnDataChange := DataChange;
  FDataLink.OnUpdateData := UpdateData;
  FQuickDraw := True;
end;

destructor TDBImage2.Destroy;
begin
  FPicture.Free;
  FDataLink.Free;
  FDataLink := nil;
  inherited Destroy;
end;

function TDBImage2.GetDataSource: TDataSource;
begin
  Result := FDataLink.DataSource;
end;

procedure TDBImage2.SetDataSource(Value: TDataSource);
begin
  if not (FDataLink.DataSourceFixed and (csLoading in ComponentState)) then
    FDataLink.DataSource := Value;
  if Value <> nil then
    Value.FreeNotification(Self);
end;

function TDBImage2.GetDataField: string;
begin
  Result := FDataLink.FieldName;
end;

procedure TDBImage2.SetDataField(const Value: string);
begin
  FDataLink.FieldName := Value;
end;

function TDBImage2.GetReadOnly: Boolean;
begin
  Result := FDataLink.ReadOnly;
end;

procedure TDBImage2.SetReadOnly(Value: Boolean);
begin
  FDataLink.ReadOnly := Value;
end;

function TDBImage2.GetField: TField;
begin
  Result := FDataLink.Field;
end;

function TDBImage2.GetPalette: HPALETTE;
begin
  Result := 0;
  if FPicture.Graphic is TBitmap then
    Result := TBitmap(FPicture.Graphic).Palette;
end;

procedure TDBImage2.SetAutoDisplay(Value: Boolean);
begin
  if FAutoDisplay <> Value then
  begin
    FAutoDisplay := Value;
    if Value then
      LoadPicture;
  end;
end;

procedure TDBImage2.SetBorderStyle(Value: TBorderStyle);
begin
  if FBorderStyle <> Value then
  begin
    FBorderStyle := Value;
    RecreateWnd;
  end;
end;

procedure TDBImage2.SetCenter(Value: Boolean);
begin
  if FCenter <> Value then
  begin
    FCenter := Value;
    Invalidate;
  end;
end;

procedure TDBImage2.SetPicture(Value: TPicture);
begin
  FPicture.Assign(Value);
end;

procedure TDBImage2.SetStretch(Value: Boolean);
begin
  if FStretch <> Value then
  begin
    FStretch := Value;
    Invalidate;
  end;
end;

procedure TDBImage2.Paint;
var
  Size: TSize;
  R: TRect;
  S: string;
  DrawPict: TPicture;
  Form: TCustomForm;
  Pal: HPalette;
begin
  with Canvas do
  begin
    Brush.Style := bsSolid;
    Brush.Color := Color;
    if FPictureLoaded or (csPaintCopy in ControlState) then
    begin
      DrawPict := TPicture.Create;
      Pal := 0;
      try
        if (csPaintCopy in ControlState) and Assigned(FDataLink.Field) and FDataLink.Field.IsBlob then
        begin
          DrawPict.Assign(FDataLink.Field);
          if DrawPict.Graphic is TBitmap then
            DrawPict.Bitmap.IgnorePalette := QuickDraw;
        end
        else
        begin
          DrawPict.Assign(Picture);
          if Focused and (DrawPict.Graphic <> nil) and (DrawPict.Graphic.Palette <> 0) then
          begin { Control has focus, so realize the bitmap palette in foreground }
            Pal := SelectPalette(Handle, DrawPict.Graphic.Palette, False);
            RealizePalette(Handle);
          end;
        end;
        if Stretch then
          if (DrawPict.Graphic = nil) or DrawPict.Graphic.Empty then
            FillRect(ClientRect)
          else
            StretchDraw(ClientRect, DrawPict.Graphic)
        else
        begin
          SetRect(R, 0, 0, DrawPict.Width, DrawPict.Height);
          if Center then
            OffsetRect(R, (ClientWidth - DrawPict.Width) div 2, (ClientHeight - DrawPict.Height) div 2);
          StretchDraw(R, DrawPict.Graphic);
          ExcludeClipRect(Handle, R.Left, R.Top, R.Right, R.Bottom);
          FillRect(ClientRect);
          SelectClipRgn(Handle, 0);
        end;
      finally
        if Pal <> 0 then
          SelectPalette(Handle, Pal, True);
        DrawPict.Free;
      end;
    end
    else
    begin
      Font := Self.Font;
      if FDataLink.Field <> nil then
        S := FDataLink.Field.DisplayLabel
      else
        S := Name;
      S := '(' + S + ')';
      Size := TextExtent(S);
      R := ClientRect;
      TextRect(R, (R.Right - Size.cx) div 2, (R.Bottom - Size.cy) div 2, S);
    end;
    Form := GetParentForm(Self);
    if (Form <> nil) and (Form.ActiveControl = Self) and not (csDesigning in ComponentState) and
      not (csPaintCopy in ControlState) then
    begin
      Brush.Color := clWindowFrame;
      FrameRect(ClientRect);
    end;
  end;
end;

procedure TDBImage2.PictureChanged(Sender: TObject);
begin
  if FPictureLoaded then
    FDataLink.Modified;
  FPictureLoaded := True;
  Invalidate;
end;

procedure TDBImage2.Notification(AComponent: TComponent; Operation: TOperation);
begin
  inherited Notification(AComponent, Operation);
  if (Operation = opRemove) and (FDataLink <> nil) and (AComponent = DataSource) then
    DataSource := nil;
end;

procedure TDBImage2.LoadPicture;
begin
  if not FPictureLoaded and (not Assigned(FDataLink.Field) or FDataLink.Field.IsBlob) then
    Picture.Assign(FDataLink.Field);
end;

procedure TDBImage2.DataChange(Sender: TObject);
begin
  Picture.Graphic := nil;
  FPictureLoaded := False;
  if FAutoDisplay then
    LoadPicture;
end;

procedure TDBImage2.UpdateData(Sender: TObject);
begin
  if Picture.Graphic is TBitmap then
    FDataLink.Field.Assign(Picture.Graphic)
  else
    FDataLink.Field.Clear;
end;

procedure TDBImage2.CopyToClipboard;
begin
  if Picture.Graphic <> nil then
    Clipboard.Assign(Picture);
end;

procedure TDBImage2.CutToClipboard;
begin
  if Picture.Graphic <> nil then
    if FDataLink.Edit then
    begin
      CopyToClipboard;
      Picture.Graphic := nil;
    end;
end;

procedure TDBImage2.PasteFromClipboard;
begin
  if Clipboard.HasFormat(CF_BITMAP) and FDataLink.Edit then
    Picture.Bitmap.Assign(Clipboard);
end;

procedure TDBImage2.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  with Params do
  begin
    if FBorderStyle = bsSingle then
      if NewStyleControls and Ctl3D then
        ExStyle := ExStyle or WS_EX_CLIENTEDGE
      else
        Style := Style or WS_BORDER;
    WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW);
  end;
end;

procedure TDBImage2.KeyDown(var Key: Word; Shift: TShiftState);
begin
  inherited KeyDown(Key, Shift);
  case Key of
    VK_INSERT:
      if ssShift in Shift then
        PasteFromClipBoard
      else if ssCtrl in Shift then
        CopyToClipBoard;
    VK_DELETE:
      if ssShift in Shift then
        CutToClipBoard;
  end;
end;

procedure TDBImage2.KeyPress(var Key: Char);
begin
  inherited KeyPress(Key);
  case Key of
    ^X: CutToClipBoard;
    ^C: CopyToClipBoard;
    ^V: PasteFromClipBoard;
    #13: LoadPicture;
    #27: FDataLink.Reset;
  end;
end;

procedure TDBImage2.CMGetDataLink(var Message: TMessage);
begin
  Message.Result := Integer(FDataLink);
end;

procedure TDBImage2.CMEnter(var Message: TCMEnter);
begin
  Invalidate; { Draw the focus marker }
  inherited;
end;

procedure TDBImage2.CMExit(var Message: TCMExit);
begin
  try
    FDataLink.UpdateRecord;
  except
    SetFocus;
    raise;
  end;
  Invalidate; { Erase the focus marker }
  inherited;
end;

procedure TDBImage2.CMTextChanged(var Message: TMessage);
begin
  inherited;
  if not FPictureLoaded then
    Invalidate;
end;

procedure TDBImage2.WMLButtonDown(var Message: TWMLButtonDown);
begin
  if TabStop and CanFocus then
    SetFocus;
  inherited;
end;

procedure TDBImage2.WMLButtonDblClk(var Message: TWMLButtonDblClk);
begin
  LoadPicture;
  inherited;
end;

procedure TDBImage2.WMCut(var Message: TMessage);
begin
  CutToClipboard;
end;

procedure TDBImage2.WMCopy(var Message: TMessage);
begin
  CopyToClipboard;
end;

procedure TDBImage2.WMPaste(var Message: TMessage);
begin
  PasteFromClipboard;
end;

procedure TDBImage2.WMSize(var Message: TMessage);
begin
  inherited;
  Invalidate;
end;

function TDBImage2.ExecuteAction(Action: TBasicAction): Boolean;
begin
  Result := inherited ExecuteAction(Action) or (FDataLink <> nil) and
    FDataLink.ExecuteAction(Action);
end;

function TDBImage2.UpdateAction(Action: TBasicAction): Boolean;
begin
  Result := inherited UpdateAction(Action) or (FDataLink <> nil) and FDataLink.UpdateAction(Action);
end;

end.

end.

Nincsenek megjegyzések:

Megjegyzés küldése