1{%MainUnit ../graphics.pp}
2
3{ TPicture and help classes TPictureFileFormatList
4
5 *****************************************************************************
6  This file is part of the Lazarus Component Library (LCL)
7
8  See the file COPYING.modifiedLGPL.txt, included in this distribution,
9  for details about the license.
10 *****************************************************************************
11}
12
13type
14  { TPicFileFormatsList }
15
16  PPicFileFormat = ^TPicFileFormat;
17  TPicFileFormat = record
18    GraphicClass: TGraphicClass;
19    Extension: string; // ; low case separated list, first is default
20    Description: string;
21  end;
22
23  TPicFileFormatsList = class(TList)
24    // list of TPicFileFormat
25  public
26    constructor Create;
27    procedure Clear; override;
28    procedure Delete(Index: Integer);
29    procedure Add(const Ext, Desc: String; AClass: TGraphicClass);
30    function GetFormats(Index: integer): PPicFileFormat;
31    function GetFormatExt(Index: integer): String;
32    function GetFormatFilter(Index: integer): String;
33    function FindExt(const Ext: string): TGraphicClass;
34    function FindClassName(const AClassname: string): TGraphicClass;
35    function FindByStreamFormat(Stream: TStream): TGraphicClass;
36    procedure Remove(AClass: TGraphicClass);
37    procedure BuildFilterStrings(GraphicClass: TGraphicClass;
38                                 var Descriptions, Filters: string);
39    property Formats[Index: integer]: PPicFileFormat read GetFormats; default;
40  end;
41
42constructor TPicFileFormatsList.Create;
43begin
44  inherited Create;
45  // add by priority of use in LCL/IDE
46  Add(TPortableNetworkGraphic.GetFileExtensions, rsPortableNetworkGraphic, TPortableNetworkGraphic);
47  Add(TPixmap.GetFileExtensions, rsPixmap, TPixmap);
48  Add(TBitmap.GetFileExtensions, rsBitmaps, TBitmap);
49  Add(TCursorImage.GetFileExtensions, rsCursor, TCursorImage);
50  Add(TIcon.GetFileExtensions, rsIcon, TIcon);
51  Add(TIcnsIcon.GetFileExtensions, rsIcns, TIcnsIcon);
52  {$IFNDEF DisableLCLJPEG}
53  Add(TJpegImage.GetFileExtensions, rsJpeg, TJpegImage);
54  {$ENDIF}
55  {$IFNDEF DisableLCLTIFF}
56  Add(TTiffImage.GetFileExtensions, rsTiff, TTiffImage);
57  {$ENDIF}
58  {$IFNDEF DisableLCLGIF}
59  Add(TGIFImage.GetFileExtensions, rsGIF, TGIFImage);
60  {$ENDIF}
61  {$IFNDEF DisableLCLPNM}
62  Add(TPortableAnyMapGraphic.GetFileExtensions, rsPortablePixmap, TPortableAnyMapGraphic);
63  {$ENDIF}
64end;
65
66procedure TPicFileFormatsList.Clear;
67var
68  i: integer;
69  P: PPicFileFormat;
70begin
71  for i:=0 to Count - 1 do
72  begin
73    P := GetFormats(i);
74    Dispose(P);
75  end;
76  inherited Clear;
77end;
78
79procedure TPicFileFormatsList.Delete(Index: Integer);
80var
81  P: PPicFileFormat;
82begin
83  P:=GetFormats(Index);
84  Dispose(P);
85  inherited Delete(Index);
86end;
87
88procedure TPicFileFormatsList.Add(const Ext, Desc: String;
89  AClass: TGraphicClass);
90var
91  NewFormat: PPicFileFormat;
92begin
93  New(NewFormat);
94  with NewFormat^ do
95  begin
96    Extension := AnsiLowerCase(Ext);
97    GraphicClass := AClass;
98    Description := Desc;
99  end;
100  inherited Add(NewFormat);
101end;
102
103function TPicFileFormatsList.GetFormats(Index: integer): PPicFileFormat;
104begin
105  Result:=PPicFileFormat(Items[Index]);
106end;
107
108function TPicFileFormatsList.GetFormatExt(Index: integer): String;
109begin
110  Result := PPicFileFormat(Items[Index])^.Extension;
111  if Pos(';', Result) > 0 then
112    System.Delete(Result, Pos(';', Result), MaxInt);
113end;
114
115function TPicFileFormatsList.GetFormatFilter(Index: integer): String;
116begin
117  Result := StringReplace('*.' + PPicFileFormat(Items[Index])^.Extension, ';', ';*.', [rfReplaceAll]);
118end;
119
120function TPicFileFormatsList.FindExt(const Ext: string): TGraphicClass;
121var
122  I, P: Integer;
123  E, ExtList: String;
124begin
125  if Ext<>'' then
126  begin
127    E := AnsiLowerCase(Ext);
128    if E[1] = '.' then System.Delete(E, 1, 1);
129
130    for I := Count - 1 downto 0 do
131      with Formats[I]^ do
132        if Pos(E, Extension) > 0 then
133        begin
134          ExtList := Extension;
135          repeat
136            P := Pos(';', ExtList);
137            if (P = 0) and (ExtList = E) or (Pos(E + ';', ExtList) = 1) then
138            begin
139              Result := GraphicClass;
140              Exit;
141            end;
142            System.Delete(ExtList, 1, P);
143          until P = 0;
144        end;
145  end;
146  Result := nil;
147end;
148
149function TPicFileFormatsList.FindClassName(const AClassName: string): TGraphicClass;
150var
151  I: Integer;
152begin
153  // search backwards so that new formats will be found first
154  for I := Count-1 downto 0 do begin
155    Result := GetFormats(I)^.GraphicClass;
156    if AnsiCompareText(Result.ClassName,AClassname)=0 then
157      Exit;
158  end;
159  Result := nil;
160end;
161
162function TPicFileFormatsList.FindByStreamFormat(Stream: TStream): TGraphicClass;
163var
164  I: Integer;
165begin
166  for I := 0 to Count - 1 do
167  begin
168    Result := GetFormats(I)^.GraphicClass;
169    if Result.IsStreamFormatSupported(Stream) then
170      Exit;
171  end;
172  Result := nil;
173end;
174
175procedure TPicFileFormatsList.Remove(AClass: TGraphicClass);
176// remove all file formats which inherits from AClass
177var
178  I: Integer;
179  P: PPicFileFormat;
180begin
181  for I := Count - 1 downto 0 do
182  begin
183    P := GetFormats(I);
184    if P^.GraphicClass.InheritsFrom(AClass) then
185      Delete(I);
186  end;
187end;
188
189procedure TPicFileFormatsList.BuildFilterStrings(GraphicClass: TGraphicClass;
190  var Descriptions, Filters: string);
191var
192  C, I: Integer;
193  P: PPicFileFormat;
194  Filter: String;
195begin
196  Descriptions := '';
197  Filters := '';
198  C := 0;
199  for I := 0 to Count - 1 do
200  begin
201    P := GetFormats(I);
202    if P^.GraphicClass.InheritsFrom(GraphicClass) and (P^.Extension <> '') then
203      with P^ do begin
204        if C <> 0 then begin
205          Descriptions := Descriptions + '|';
206          Filters := Filters + ';';
207        end;
208        Filter := GetFormatFilter(I);
209        FmtStr(Descriptions, '%s%s (%s)|%s',
210             [Descriptions, Description, Filter, Filter]);
211        FmtStr(Filters, '%s%s', [Filters, Filter]);
212        Inc(C);
213      end;
214  end;
215  if C > 1 then
216    FmtStr(Descriptions, '%s (%s)|%1:s|%s',
217     [rsGraphic, Filters, Descriptions]);
218end;
219
220//------------------------------------------------------------------------------
221
222type
223  PPicClipboardFormat = ^TPicClipboardFormat;
224  TPicClipboardFormat = record
225    GraphicClass: TGraphicClass;
226    FormatID: TClipboardFormat;
227  end;
228
229  TPicClipboardFormats = class(TList)
230    // list of TPicClipboardFormat
231  private
232    function GetFormats(Index: integer): PPicClipboardFormat;
233  public
234    constructor Create;
235    procedure Clear; override;
236    procedure Delete(Index: Integer);
237    procedure Add(AFormatID: TClipboardFormat; AClass: TGraphicClass);
238    function FindFormat(FormatID: TClipboardFormat): TGraphicClass;
239    procedure Remove(AClass: TGraphicClass);
240    property Formats[Index: integer]: PPicClipboardFormat read GetFormats; default;
241  end;
242
243function TPicClipboardFormats.GetFormats(Index: integer): PPicClipboardFormat;
244begin
245  Result:=PPicClipboardFormat(Items[Index]);
246end;
247
248constructor TPicClipboardFormats.Create;
249const
250  sMimeTypePng = 'image/png';
251  sMimeTypeJpg = 'image/jpeg';
252begin
253  inherited Create;
254  Add(PredefinedClipboardFormat(pcfBitmap), TBitmap);
255  Add(PredefinedClipboardFormat(pcfPixmap), TPixmap);
256  //Add(PredefinedClipboardFormat(pcfIcon), TCustomIcon);
257  Add(ClipboardRegisterFormat(sMimeTypePng), TPortableNetworkGraphic);
258  {$IFNDEF DisableLCLJPEG}
259  Add(ClipboardRegisterFormat(sMimeTypeJpg), TJPegImage);
260  {$ENDIF}
261end;
262
263procedure TPicClipboardFormats.Clear;
264var
265  i: integer;
266  P: PPicClipboardFormat;
267begin
268  for i := 0 to Count - 1 do
269  begin
270    P := GetFormats(i);
271    Dispose(P);
272  end;
273  inherited Clear;
274end;
275
276procedure TPicClipboardFormats.Delete(Index: Integer);
277var
278  P: PPicClipboardFormat;
279begin
280  P := GetFormats(Index);
281  Dispose(P);
282  inherited Delete(Index);
283end;
284
285procedure TPicClipboardFormats.Add(AFormatID: TClipboardFormat;
286  AClass: TGraphicClass);
287var NewFormat: PPicClipboardFormat;
288begin
289  if AFormatID=0 then exit;
290  New(NewFormat);
291  with NewFormat^ do begin
292    GraphicClass:=AClass;
293    FormatID:=AFormatID;
294  end;
295  inherited Add(NewFormat);
296end;
297
298function TPicClipboardFormats.FindFormat(
299  FormatID: TClipboardFormat): TGraphicClass;
300var
301  I: Integer;
302  P: PPicClipboardFormat;
303begin
304  for I := Count-1 downto 0 do begin
305    P:=GetFormats(i);
306    if P^.FormatID=FormatID then begin
307      Result:=P^.GraphicClass;
308      Exit;
309    end;
310  end;
311  Result := nil;
312end;
313
314procedure TPicClipboardFormats.Remove(AClass: TGraphicClass);
315var
316  I: Integer;
317begin
318  for I := Count-1 downto 0 do
319    if GetFormats(i)^.GraphicClass.InheritsFrom(AClass) then
320      Delete(i);
321end;
322
323//------------------------------------------------------------------------------
324
325var
326  PicClipboardFormats: TPicClipboardFormats=nil;
327  PicFileFormats: TPicFileFormatsList=nil;
328
329function GetPicFileFormats: TPicFileFormatsList;
330begin
331  if not Assigned(PicFileFormats) and not GraphicsFinalized then
332    PicFileFormats := TPicFileFormatsList.Create;
333  Result := PicFileFormats;
334end;
335
336function GetPicClipboardFormats: TPicClipboardFormats;
337begin
338  if (PicClipboardFormats = nil) and (not GraphicsFinalized) then
339    PicClipboardFormats := TPicClipboardFormats.Create;
340  Result := PicClipboardFormats;
341end;
342
343function GraphicFilter(GraphicClass: TGraphicClass): string;
344var
345  Filters: string;
346begin
347  Result := '';
348  GetPicFileFormats.BuildFilterStrings(GraphicClass,Result,Filters);
349end;
350
351function GraphicExtension(GraphicClass: TGraphicClass): string;
352var
353  I: Integer;
354  PicFormats: TPicFileFormatsList;
355begin
356  PicFormats := GetPicFileFormats;
357  for I := PicFormats.Count-1 downto 0 do
358    if PicFormats[I]^.GraphicClass.ClassName = GraphicClass.ClassName then
359    begin
360      Result := PicFormats.GetFormatExt(I);
361      Exit;
362    end;
363  Result := '';
364end;
365
366function GraphicFileMask(GraphicClass: TGraphicClass): string;
367var
368  Descriptions: string;
369begin
370  Result := '';
371  GetPicFileFormats.BuildFilterStrings(GraphicClass,Descriptions,Result);
372end;
373
374function GetGraphicClassForFileExtension(const FileExt: string): TGraphicClass;
375begin
376  Result:=GetPicFileFormats.FindExt(FileExt);
377end;
378
379//--TPicture--------------------------------------------------------------------
380
381
382constructor TPicture.Create;
383begin
384  inherited Create;
385  GetPicFileFormats;
386  GetPicClipboardFormats;
387end;
388
389destructor TPicture.Destroy;
390begin
391  FGraphic.Free;
392  inherited Destroy;
393end;
394
395procedure TPicture.AssignTo(Dest: TPersistent);
396begin
397  if Graphic is Dest.ClassType then
398    Dest.Assign(Graphic)
399  else
400    inherited AssignTo(Dest);
401end;
402
403procedure TPicture.ForceType(GraphicType: TGraphicClass);
404var
405  NewGraphic: TGraphic;
406begin
407  if not (FGraphic is GraphicType) then
408  begin
409    NewGraphic := GraphicType.Create;
410    NewGraphic.Assign(FGraphic);
411    FGraphic.Free;
412    FGraphic := NewGraphic;
413    FGraphic.OnChange := @Changed;
414    FGraphic.OnProgress := @Progress;
415    Changed(Self);
416  end;
417end;
418
419function TPicture.GetBitmap: TBitmap;
420begin
421  ForceType(TBitmap);
422  Result := TBitmap(Graphic);
423end;
424
425function TPicture.GetPNG: TPortableNetworkGraphic;
426begin
427  ForceType(TPortableNetworkGraphic);
428  Result := TPortableNetworkGraphic(Graphic);
429end;
430
431{$IFNDEF DisableLCLPNM}
432function TPicture.GetPNM: TPortableAnyMapGraphic;
433begin
434  ForceType(TPortableAnyMapGraphic);
435  Result := TPortableAnyMapGraphic(Graphic);
436end;
437{$ENDIF}
438
439function TPicture.GetPixmap: TPixmap;
440begin
441  ForceType(TPixmap);
442  Result := TPixmap(Graphic);
443end;
444
445function TPicture.GetIcon: TIcon;
446begin
447  ForceType(TIcon);
448  Result := TIcon(Graphic);
449end;
450
451{$IFNDEF DisableLCLJPEG}
452function TPicture.GetJpeg: TJpegImage;
453begin
454  ForceType(TJpegImage);
455  Result := TJpegImage(Graphic);
456end;
457{$ENDIF}
458
459procedure TPicture.SetBitmap(Value: TBitmap);
460begin
461  SetGraphic(Value);
462end;
463
464procedure TPicture.SetPNG(const AValue: TPortableNetworkGraphic);
465begin
466  SetGraphic(AValue);
467end;
468
469{$IFNDEF DisableLCLPNM}
470procedure TPicture.SetPNM(const AValue: TPortableAnyMapGraphic);
471begin
472  SetGraphic(AValue);
473end;
474{$ENDIF}
475
476procedure TPicture.SetPixmap(Value: TPixmap);
477begin
478  SetGraphic(Value);
479end;
480
481procedure TPicture.SetIcon(Value: TIcon);
482begin
483  SetGraphic(Value);
484end;
485
486{$IFNDEF DisableLCLJPEG}
487procedure TPicture.SetJpeg(Value: TJpegImage);
488begin
489  SetGraphic(Value);
490end;
491{$ENDIF}
492
493procedure TPicture.SetGraphic(Value: TGraphic);
494var
495  NewGraphic: TGraphic;
496  ok: boolean;
497begin
498  if (Value=FGraphic) then exit;
499  NewGraphic := nil;
500  ok := False;
501  try
502    if Value <> nil then
503    begin
504      NewGraphic := TGraphicClass(Value.ClassType).Create;
505      NewGraphic.Assign(Value);
506      NewGraphic.OnChange := @Changed;
507      NewGraphic.OnProgress := @Progress;
508    end;
509    FGraphic.Free;
510    FGraphic := NewGraphic;
511    Changed(Self);
512    ok := True;
513  finally
514    // this try..finally construction will in case of an exception
515    // not alter the error backtrace output
516    if not ok then
517      NewGraphic.Free;
518  end;
519end;
520
521{ Based on the extension of Filename, create the corresponding TGraphic class
522  and call its LoadFromFile method. }
523
524procedure TPicture.LoadFromFile(const Filename: string);
525var
526  Ext: string;
527  Stream: TStream;
528begin
529  Ext := ExtractFileExt(Filename);
530  System.Delete(Ext, 1, 1); // delete '.'
531
532  Stream := TFileStream.Create(Filename, fmOpenRead or fmShareDenyWrite);
533  try
534    if Ext <> '' then
535      LoadFromStreamWithFileExt(Stream, Ext)
536    else
537      LoadFromStream(Stream);
538  finally
539    Stream.Free;
540  end;
541end;
542
543procedure TPicture.LoadFromResourceName(Instance: THandle; const ResName: String);
544var
545  NewGraphic: TGraphic;
546begin
547  NewGraphic := CreateGraphicFromResourceName(Instance, ResName);
548  FGraphic.Free;
549  FGraphic := NewGraphic;
550  FGraphic.OnChange := @Changed;
551  Changed(Self);
552end;
553
554procedure TPicture.LoadFromResourceName(Instance: THandle;
555  const ResName: String; AClass: TGraphicClass);
556var
557  NewGraphic: TGraphic;
558  ok: Boolean;
559begin
560  NewGraphic := AClass.Create;
561  ok:=false;
562  try
563    NewGraphic.OnProgress := @Progress;
564    NewGraphic.LoadFromResourceName(Instance, ResName);
565    ok:=true;
566  finally
567    // this try..finally construction will in case of an exception
568    // not alter the error backtrace output
569    if not ok then NewGraphic.Free;
570  end;
571  FGraphic.Free;
572  FGraphic := NewGraphic;
573  FGraphic.OnChange := @Changed;
574  Changed(Self);
575end;
576
577procedure TPicture.LoadFromLazarusResource(const AName: string);
578var
579  Stream: TLazarusResourceStream;
580begin
581  Stream := TLazarusResourceStream.Create(AName, nil);
582  try
583    LoadFromStreamWithFileExt(Stream, Stream.Res.ValueType);
584  finally
585    Stream.Free;
586  end;
587end;
588
589procedure TPicture.LoadFromStream(Stream: TStream);
590var
591  GraphicClass: TGraphicClass;
592begin
593  GraphicClass := GetPicFileFormats.FindByStreamFormat(Stream);
594  if GraphicClass = nil then
595    raise EInvalidGraphic.Create(rsUnknownPictureFormat);
596  LoadFromStreamWithClass(Stream, GraphicClass);
597end;
598
599procedure TPicture.SaveToFile(const Filename: string; const FileExt: string = '');
600var
601  Ext: string;
602  Stream: TStream;
603begin
604  if FileExt <> '' then
605    Ext := AnsiLowerCase(FileExt)
606  else
607    Ext := AnsiLowerCase(ExtractFileExt(Filename));
608
609  if (Ext <> '') and (Ext[1] = '.') then System.Delete(Ext, 1, 1); // delete '.'
610
611  Stream := TFileStream.Create(Filename, fmCreate);
612  try
613    SaveToStreamWithFileExt(Stream, Ext);
614  finally
615    Stream.Free;
616  end;
617end;
618
619procedure TPicture.SaveToStream(Stream: TStream);
620begin
621  if Assigned(Graphic) then
622    Graphic.SaveToStream(Stream);
623end;
624
625procedure TPicture.SaveToStreamWithFileExt(Stream: TStream; const FileExt: string);
626var
627  GraphicClass: TGraphicClass;
628  IntfImg: TLazIntfImage;
629  ImgWriter: TFPCustomImageWriter;
630  fpBmp: TFPImageBitmap;
631begin
632  if Graphic = nil then Exit;
633  if FileExt <> '' then
634    GraphicClass := FindGraphicClassWithFileExt(FileExt);
635
636  if (FileExt = '')
637  or (Graphic is GraphicClass)
638  then begin
639    Graphic.SaveToStream(Stream);
640    Exit;
641  end;
642
643  // save in different format
644  if (Graphic is TFPImageBitmap) and GraphicClass.InheritsFrom(TFPImageBitmap)
645  then begin
646    fpBmp := TFPImageBitmap(Graphic);
647    ImgWriter := nil;
648    IntfImg := TLazIntfImage.Create(0,0,[]);
649    try
650      ImgWriter := TFPImageBitmapClass(GraphicClass).GetWriterClass.Create;
651      IntfImg.SetRawImage(fpBmp.GetRawImagePtr^, False);
652      fpBmp.InitializeWriter(IntfImg, ImgWriter);
653      IntfImg.SaveToStream(Stream, ImgWriter);
654      fpBmp.FinalizeWriter(ImgWriter);
655    finally
656      IntfImg.Free;
657      ImgWriter.Free;
658    end;
659    Exit;
660  end;
661
662  // no conversion available yet
663  raise Exception.CreateFmt('TODO: Conversion for vector or icon images of format "%s" to "%s"!', [Graphic.GetFileExtensions, FileExt]);
664end;
665
666procedure TPicture.LoadFromStreamWithFileExt(Stream: TStream;
667  const FileExt: string);
668begin
669  LoadFromStreamWithClass(Stream, FindGraphicClassWithFileExt(FileExt));
670end;
671
672procedure TPicture.LoadFromClipboardFormat(FormatID: TClipboardFormat);
673begin
674  LoadFromClipboardFormatID(ctClipboard,FormatID);
675end;
676
677procedure TPicture.LoadFromClipboardFormatID(ClipboardType: TClipboardType;
678  FormatID: TClipboardFormat);
679var
680  NewGraphic: TGraphic;
681  GraphicClass: TGraphicClass;
682  ok: boolean;
683begin
684  GraphicClass := PicClipboardFormats.FindFormat(FormatID);
685  if GraphicClass = nil then
686    raise EInvalidGraphic.CreateFmt(rsUnsupportedClipboardFormat,
687      [ClipboardFormatToMimeType(FormatID)]);
688
689  NewGraphic := GraphicClass.Create;
690  ok:=false;
691  try
692    NewGraphic.OnProgress := @Progress;
693    NewGraphic.LoadFromClipboardFormatID(ClipboardType,FormatID);
694    ok:=true;
695  finally
696    if not ok then NewGraphic.Free;
697  end;
698  FGraphic.Free;
699  FGraphic := NewGraphic;
700  FGraphic.OnChange := @Changed;
701  Changed(Self);
702end;
703
704procedure TPicture.SaveToClipboardFormat(FormatID: TClipboardFormat);
705begin
706  if FGraphic <> nil then
707    FGraphic.SaveToClipboardFormat(FormatID);
708end;
709
710class function TPicture.SupportsClipboardFormat(FormatID: TClipboardFormat): Boolean;
711begin
712  Result := GetPicClipboardFormats.FindFormat(FormatID) <> nil;
713end;
714
715procedure TPicture.Assign(Source: TPersistent);
716begin
717  if Source = nil then
718    SetGraphic(nil)
719  else if Source is TPicture then
720    SetGraphic(TPicture(Source).Graphic)
721  else if Source is TGraphic then
722    SetGraphic(TGraphic(Source))
723  else if Source is TFPCustomImage then
724    Bitmap.Assign(Source)
725  else
726    inherited Assign(Source);
727end;
728
729class procedure TPicture.RegisterFileFormat(const AnExtension,
730  ADescription: string; AGraphicClass: TGraphicClass);
731begin
732  GetPicFileFormats.Add(AnExtension, ADescription, AGraphicClass);
733end;
734
735class procedure TPicture.RegisterClipboardFormat(FormatID: TClipboardFormat;
736  AGraphicClass: TGraphicClass);
737begin
738  GetPicClipboardFormats.Add(FormatID, AGraphicClass);
739end;
740
741class procedure TPicture.UnregisterGraphicClass(AClass: TGraphicClass);
742begin
743  if PicFileFormats <> nil then PicFileFormats.Remove(AClass);
744  if PicClipboardFormats <> nil then PicClipboardFormats.Remove(AClass);
745end;
746
747procedure TPicture.Clear;
748begin
749  SetGraphic(nil);
750end;
751
752class function TPicture.FindGraphicClassWithFileExt(const Ext: string;
753  ExceptionOnNotFound: boolean): TGraphicClass;
754var
755  FileExt: String;
756begin
757  FileExt := Ext;
758  if (FileExt <> '') and (FileExt[1] = '.') then
759    FileExt := Copy(FileExt, 2, length(FileExt));
760  Result := GetPicFileFormats.FindExt(FileExt);
761  if (Result = nil) and ExceptionOnNotFound then
762    raise EInvalidGraphic.CreateFmt(rsUnknownPictureExtension, [Ext]);
763end;
764
765procedure TPicture.Changed(Sender: TObject);
766begin
767  if Assigned(FOnChange) then FOnChange(Self);
768end;
769
770procedure TPicture.Progress(Sender: TObject; Stage: TProgressStage;
771  PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string;
772  var DoContinue: boolean);
773begin
774  DoContinue:=true;
775  if Assigned(FOnProgress) then
776    FOnProgress(Sender, Stage, PercentDone, RedrawNow, R, Msg, DoContinue);
777end;
778
779procedure TPicture.LoadFromStreamWithClass(Stream: TStream; AClass: TGraphicClass);
780var
781  NewGraphic: TGraphic;
782  ok: Boolean;
783begin
784  NewGraphic := AClass.Create;
785  ok:=false;
786  try
787    NewGraphic.OnProgress := @Progress;
788    NewGraphic.LoadFromStream(Stream);
789    ok:=true;
790  finally
791    // this try..finally construction will in case of an exception
792    // not alter the error backtrace output
793    if not ok then NewGraphic.Free;
794  end;
795  FGraphic.Free;
796  FGraphic := NewGraphic;
797  FGraphic.OnChange := @Changed;
798  Changed(Self);
799end;
800
801procedure TPicture.ReadData(Stream: TStream);
802var
803  GraphicClassName: Shortstring;
804  NewGraphic: TGraphic;
805  GraphicClass: TGraphicClass;
806  ok: boolean;
807begin
808  Stream.Read(GraphicClassName[0], 1);
809  Stream.Read(GraphicClassName[1], length(GraphicClassName));
810  GraphicClass := GetPicFileFormats.FindClassName(GraphicClassName);
811  NewGraphic := nil;
812  if GraphicClass <> nil then begin
813    NewGraphic := GraphicClass.Create;
814    ok:=false;
815    try
816      NewGraphic.ReadData(Stream);
817      ok:=true;
818    finally
819      if not ok then NewGraphic.Free;
820    end;
821  end;
822  FGraphic.Free;
823  FGraphic := NewGraphic;
824  if NewGraphic <> nil then begin
825    NewGraphic.OnChange := @Changed;
826    NewGraphic.OnProgress := @Progress;
827  end;
828  Changed(Self);
829end;
830
831procedure TPicture.WriteData(Stream: TStream);
832var
833  GraphicClassName: ShortString;
834begin
835  with Stream do
836  begin
837    if Graphic <> nil then
838      GraphicClassName := Graphic.ClassName
839    else
840      GraphicClassName := '';
841    Write(GraphicClassName, Length(GraphicClassName) + 1);
842    if Graphic <> nil then
843      Graphic.WriteData(Stream);
844  end;
845end;
846
847procedure TPicture.DefineProperties(Filer: TFiler);
848
849  function DoWrite: Boolean;
850  var
851    Ancestor: TPicture;
852  begin
853    if Filer.Ancestor is TPicture then
854    begin
855      Ancestor := TPicture(Filer.Ancestor);
856      if not Assigned(Graphic) then
857        Exit(Assigned(Ancestor.Graphic));
858      Result := not Graphic.Equals(Ancestor.Graphic);
859    end
860    else
861      Result := Assigned(Graphic);
862  end;
863
864begin
865  Filer.DefineBinaryProperty('Data', @ReadData, @WriteData, DoWrite);
866end;
867
868function TPicture.GetWidth: Integer;
869begin
870  if FGraphic <> nil then
871    Result := FGraphic.Width
872  else
873    Result := 0;
874end;
875
876function TPicture.GetHeight: Integer;
877begin
878  Result := 0;
879  if FGraphic <> nil then
880    Result := FGraphic.Height
881  else
882    Result := 0;
883end;
884
885// included by graphics.pp
886