1{
2    This file is part of the Free Pascal run time library.
3    Copyright (c) 2003 by the Free Pascal development team
4
5    TFPCustomImage implementation.
6
7    See the file COPYING.FPC, included in this distribution,
8    for details about the copyright.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 **********************************************************************}
15{ TFPCustomImage }
16
17constructor TFPCustomImage.create (AWidth,AHeight:integer);
18begin
19  inherited create;
20  FExtra := TStringList.Create;
21  FWidth := 0;
22  FHeight := 0;
23  FPalette := nil;
24  SetSize (AWidth,AHeight);
25end;
26
27destructor TFPCustomImage.destroy;
28begin
29  FExtra.Free;
30  if assigned (FPalette) then
31    FPalette.Free;
32  inherited;
33end;
34
35procedure TFPCustomImage.LoadFromStream (Str:TStream; Handler:TFPCustomImagereader);
36begin
37  Handler.ImageRead (Str, self);
38end;
39
40procedure TFPCustomImage.LoadFromFile (const filename:String; Handler:TFPCustomImageReader);
41var
42  fs : TStream;
43begin
44  if FileExists (filename) then
45    begin
46    fs := TFileStream.Create (filename, fmOpenRead);
47    try
48      LoadFromStream (fs, handler);
49    finally
50      fs.Free;
51    end;
52    end
53  else
54    FPImgError (StrNoFile, [filename]);
55end;
56
57procedure TFPCustomImage.SaveToStream (Str:TStream; Handler:TFPCustomImageWriter);
58begin
59  Handler.ImageWrite (Str, Self);
60end;
61
62procedure TFPCustomImage.SaveToFile (const filename:String; Handler:TFPCustomImageWriter);
63var
64  fs : TStream;
65begin
66  fs := TFileStream.Create (filename, fmCreate);
67  try
68    SaveToStream (fs, handler);
69  finally
70    fs.Free;
71  end
72end;
73
74function TFPCustomImage.SaveToFile (const filename:String):boolean;
75
76var h : TFPCustomImageWriterClass;
77    Writer : TFPCustomImageWriter;
78    Msg : string;
79
80begin
81  Msg := '';
82  try
83    h := FindWriterFromFileName(filename);
84    Result := assigned (h);
85    if Result then
86      begin
87      Writer := h.Create;
88      try
89        SaveTofile (filename, Writer);
90      finally
91        Writer.Free;
92      end;
93      end;
94  except
95    on e : exception do
96      Msg := e.message;
97  end;
98  if (Msg<>'') then
99    FPImgError (StrWriteWithError, [Msg]);
100end;
101
102
103procedure TFPCustomImage.LoadFromStream (Str:TStream);
104var r : integer;
105    h : TFPCustomImageReaderClass;
106    reader : TFPCustomImageReader;
107    msg : string;
108    d : TIHData;
109    startPos: Int64;
110begin
111  msg := '';
112  startPos := str.Position;
113  with ImageHandlers do
114    try
115      r := count-1;
116      while (r >= 0) do
117        begin
118        d := GetData(r);
119        if assigned (d) then
120          h := d.FReader
121        else
122          h := nil;
123        if assigned (h) then
124          begin
125          reader := h.Create;
126          with reader do
127            try
128              if CheckContents (str) then
129                try
130                  FStream := str;
131                  FImage := self;
132                  InternalRead (str, self);
133                  msg := '';
134                  break;
135                except
136                  on e : exception do
137                    msg := e.message;
138                end;
139            finally
140              Free;
141              str.Position := startPos;
142            end;
143          end;
144        dec (r);
145        end;
146    except
147      on e : exception do
148        FPImgError (StrCantDetermineType, [e.message]);
149    end;
150  if r < 0 then
151    if msg = '' then
152      FPImgError (StrNoCorrectReaderFound)
153    else
154      FPImgError (StrReadWithError, [Msg]);
155end;
156
157function TFPCustomImage.LoadFromFile (const filename:String):boolean;
158var f : TFileStream;
159    h : TFPCustomImageReaderClass;
160    reader : TFPCustomImageReader;
161    Msg : string;
162begin
163  Msg := '';
164  try
165    h := FindReaderFromFileName(filename);
166    Result := assigned (h);
167    if Result then
168      begin
169      reader := h.Create;
170      try
171        loadfromfile (filename, reader);
172      finally
173        Reader.Free;
174      end;
175      end;
176  except
177    on e : exception do
178      Msg := e.message;
179  end;
180  if Msg = '' then
181    begin
182    if h = nil then
183      begin
184      f := TFileStream.Create (filename, fmOpenRead);
185      try
186        LoadFromStream (f);
187      finally
188        f.Free;
189      end;
190      end;
191    end
192  else
193    FPImgError (StrReadWithError, [Msg]);
194end;
195
196procedure TFPCustomImage.SetHeight (Value : integer);
197begin
198  if Value <> FHeight then
199    SetSize (FWidth, Value);
200end;
201
202procedure TFPCustomImage.SetWidth (Value : integer);
203begin
204  if Value <> FWidth then
205    SetSize (Value, FHeight);
206end;
207
208procedure TFPCustomImage.SetSize (AWidth, AHeight : integer);
209begin
210  FWidth := AWidth;
211  FHeight := AHeight;
212end;
213
214procedure TFPCustomImage.SetExtraValue (index:integer; const AValue:string);
215var s : string;
216    p : integer;
217begin
218  s := FExtra[index];
219  p := pos ('=', s);
220  if p > 0 then
221    FExtra[index] := copy(s, 1, p) + AValue
222  else
223    FPImgError (StrInvalidIndex,[ErrorText[StrImageExtra],index]);
224end;
225
226function TFPCustomImage.GetExtraValue (index:integer) : string;
227var s : string;
228    p : integer;
229begin
230  s := FExtra[index];
231  p := pos ('=', s);
232  if p > 0 then
233    result := copy(s, p+1, maxint)
234  else
235    result := '';
236end;
237
238procedure TFPCustomImage.SetExtraKey (index:integer; const AValue:string);
239var s : string;
240    p : integer;
241begin
242  s := FExtra[index];
243  p := pos('=',s);
244  if p > 0 then
245    s := AValue + copy(s,p,maxint)
246  else
247    s := AValue;
248  FExtra[index] := s;
249end;
250
251function TFPCustomImage.GetExtraKey (index:integer) : string;
252begin
253  result := FExtra.Names[index];
254end;
255
256procedure TFPCustomImage.SetExtra (const key:String; const AValue:string);
257begin
258  FExtra.values[key] := AValue;
259end;
260
261function TFPCustomImage.GetExtra (const key:String) : string;
262begin
263  result := FExtra.values[key];
264end;
265
266function  TFPCustomImage.ExtraCount : integer;
267begin
268  result := FExtra.count;
269end;
270
271const dumchar = ';';
272class function TFPCustomImage.FindHandlerFromExtension(extension: String
273  ): TIHData;
274var s : string;
275    r : integer;
276begin
277  if extension='' then
278    Exit(nil);
279  extension := lowercase (extension);
280  if (extension <> '') and (extension[1] = '.') then
281    delete (extension,1,1);
282  with ImageHandlers do
283    begin
284      r := count-1;
285      s := dumchar  + extension + dumchar;
286      while (r >= 0) do
287        begin
288        Result := GetData(r);
289        if (pos(s, dumchar+Result.Fextension+dumchar) <> 0) then
290          Exit;
291        dec (r);
292        end;
293    end;
294  Result := nil;
295end;
296
297class function TFPCustomImage.FindHandlerFromStream(Str: TStream): TIHData;
298var r : integer;
299    p: Int64;
300    reader: TFPCustomImageReader;
301begin
302  r := ImageHandlers.Count-1;
303  p := Str.Position;
304  while (r >= 0) do
305    begin
306    Result := ImageHandlers.GetData(r);
307    if Result.Reader<>nil then
308      begin
309      reader := Result.Reader.Create;
310      try
311        if reader.CheckContents(Str) then
312          Exit;
313      finally
314        reader.free;
315        Str.Position := p;
316      end;
317      end;
318    dec (r);
319    end;
320  Result := nil;
321end;
322
323class function TFPCustomImage.FindReaderFromExtension(const extension: String
324  ): TFPCustomImageReaderClass;
325var d : TIHData;
326begin
327  d := FindHandlerFromExtension(extension);
328  if d<>nil then
329    Result := d.FReader
330  else
331    Result := nil;
332end;
333
334class function TFPCustomImage.FindReaderFromFileName(const filename: String
335  ): TFPCustomImageReaderClass;
336begin
337  Result := FindReaderFromExtension(ExtractFileExt(filename));
338end;
339
340class function TFPCustomImage.FindReaderFromStream(
341  Str: TStream): TFPCustomImageReaderClass;
342var d : TIHData;
343begin
344  d := FindHandlerFromStream(Str);
345  if d<>nil then
346    Result := d.FReader
347  else
348    Result := nil;
349end;
350
351class function TFPCustomImage.FindWriterFromExtension(const extension: String
352  ): TFPCustomImageWriterClass;
353var d : TIHData;
354begin
355  d := FindHandlerFromExtension(extension);
356  if d<>nil then
357    Result := d.FWriter
358  else
359    Result := nil;
360end;
361
362class function TFPCustomImage.FindWriterFromFileName(const filename: String
363  ): TFPCustomImageWriterClass;
364begin
365  Result := FindWriterFromExtension(ExtractFileExt(filename));
366end;
367
368procedure TFPCustomImage.RemoveExtra (const key:string);
369var p : integer;
370begin
371  p := FExtra.IndexOfName(key);
372  if p >= 0 then
373    FExtra.Delete (p);
374end;
375
376procedure TFPCustomImage.SetPixel (x,y:integer; Value:integer);
377begin
378  CheckPaletteIndex (Value);
379  CheckIndex (x,y);
380  SetInternalPixel (x,y,Value);
381end;
382
383function TFPCustomImage.GetPixel (x,y:integer) : integer;
384begin
385  CheckIndex (x,y);
386  result := GetInternalPixel(x,y);
387end;
388
389procedure TFPCustomImage.SetColor (x,y:integer; const Value:TFPColor);
390begin
391  CheckIndex (x,y);
392  SetInternalColor (x,y,Value);
393end;
394
395function TFPCustomImage.GetColor (x,y:integer) : TFPColor;
396begin
397  CheckIndex (x,y);
398  result := GetInternalColor(x,y);
399end;
400
401procedure TFPCustomImage.SetInternalColor (x,y:integer; const Value:TFPColor);
402var i : integer;
403begin
404  i := FPalette.IndexOf (Value);
405  SetInternalPixel (x,y,i);
406end;
407
408function TFPCustomImage.GetInternalColor (x,y:integer) : TFPColor;
409begin
410  result := FPalette.Color[GetInternalPixel(x,y)];
411end;
412
413function TFPCustomImage.GetUsePalette : boolean;
414begin
415  result := assigned(FPalette);
416end;
417
418procedure TFPCustomImage.SetUsePalette(Value:boolean);
419begin
420  if Value <> assigned(FPalette)
421  then
422    if Value
423    then
424      begin
425        FPalette := TFPPalette.Create (0);
426        // FPalette.Add (colTransparent);
427      end
428    else
429      begin
430        FPalette.Free;
431        FPalette := nil;
432      end;
433end;
434
435procedure TFPCustomImage.CheckPaletteIndex (PalIndex:integer);
436begin
437  if UsePalette then
438    begin
439    if (PalIndex < -1) or (PalIndex >= FPalette.Count) then
440      FPImgError (StrInvalidIndex,[ErrorText[StrPalette],PalIndex]);
441    end
442  else
443    FPImgError (StrNoPaletteAvailable);
444end;
445
446procedure TFPCustomImage.CheckIndex (x,y:integer);
447begin
448  if (x < 0) or (x >= FWidth) then
449    FPImgError (StrInvalidIndex,[ErrorText[StrImageX],x]);
450  if (y < 0) or (y >= FHeight) then
451    FPImgError (StrInvalidIndex,[ErrorText[StrImageY],y]);
452end;
453
454Procedure TFPCustomImage.Progress(Sender: TObject; Stage: TProgressStage;
455                         PercentDone: Byte;  RedrawNow: Boolean; const R: TRect;
456                         const Msg: AnsiString; var Continue: Boolean);
457begin
458  If Assigned(FOnProgress) then
459    FonProgress(Sender,Stage,PercentDone,RedrawNow,R,Msg,Continue);
460end;
461
462Procedure TFPCustomImage.Assign(Source: TPersistent);
463
464Var
465  Src : TFPCustomImage;
466  X,Y : Integer;
467
468begin
469  If Source is TFPCustomImage then
470    begin
471    Src:=TFPCustomImage(Source);
472    // Copy extra info
473    FExtra.Assign(Src.Fextra);
474    // Copy palette if needed.
475    SetSize(0,0); { avoid side-effects in descendant classes }
476    UsePalette:=Src.UsePalette;
477    If UsePalette then
478      begin
479      Palette.Count:=0;
480      Palette.Merge(Src.Palette);
481      end;
482    // Copy image.
483    SetSize(Src.Width,Src.height);
484    If UsePalette then
485      For x:=0 to Src.Width-1 do
486        For y:=0 to src.Height-1 do
487          pixels[X,Y]:=src.pixels[X,Y]
488    else
489      For x:=0 to Src.Width-1 do
490        For y:=0 to src.Height-1 do
491          self[X,Y]:=src[X,Y];
492    end
493  else
494    Inherited Assign(Source);
495end;
496
497{ TFPMemoryImage }
498
499constructor TFPMemoryImage.Create (AWidth,AHeight:integer);
500begin
501  Fdata := nil;
502  inherited create (AWidth,AHeight);
503  SetUsePalette(False);
504end;
505
506destructor TFPMemoryImage.Destroy;
507begin
508  // MG: missing if
509  if FData<>nil then
510    FreeMem (FData);
511  inherited Destroy;
512end;
513
514function TFPMemoryImage.GetInternalColor(x,y:integer):TFPColor;
515  begin
516    if Assigned(FPalette)
517    then
518      Result:=inherited GetInternalColor(x,y)
519    else
520      Result:=PFPColorArray(FData)^[y*FWidth+x];
521  end;
522
523function TFPMemoryImage.GetInternalPixel (x,y:integer) : integer;
524begin
525  result := FData^[y*FWidth+x];
526end;
527
528procedure TFPMemoryImage.SetInternalColor (x,y:integer; const Value:TFPColor);
529  begin
530    if Assigned(FPalette)
531    then
532      inherited SetInternalColor(x,y,Value)
533    else
534      PFPColorArray(FData)^[y*FWidth+x]:=Value;
535  end;
536
537procedure TFPMemoryImage.SetInternalPixel (x,y:integer; Value:integer);
538begin
539  FData^[y*FWidth+x] := Value;
540end;
541
542function Lowest (a,b : integer) : integer;
543begin
544  if a <= b then
545    result := a
546  else
547    result := b;
548end;
549
550procedure TFPMemoryImage.SetSize (AWidth, AHeight : integer);
551var w, h, r, old : integer;
552    NewData : PFPIntegerArray;
553begin
554  if (AWidth <> Width) or (AHeight <> Height) then
555    begin
556    old := Height * Width;
557    r:=AWidth*AHeight;
558    if Assigned(FPalette)
559    then
560      r:=SizeOf(integer)*r
561    else
562      r:=SizeOf(TFPColor)*r;
563    if r = 0 then
564      NewData := nil
565    else
566      begin
567      GetMem (NewData, r);
568      FillWord (Newdata^[0], r div sizeof(word), 0);
569      end;
570    // MG: missing "and (NewData<>nil)"
571    if (old <> 0) and assigned(FData) and (NewData<>nil) then
572      begin
573      if r <> 0 then
574        begin
575        w := Lowest(Width, AWidth);
576        h := Lowest(Height, AHeight);
577        for r := 0 to h-1 do
578          move (FData^[r*Width], NewData^[r*AWidth], w);
579        end;
580      end;
581    if Assigned(FData) then FreeMem(FData);
582    FData := NewData;
583    inherited;
584    end;
585end;
586
587procedure TFPMemoryImage.SetUsePalette(Value:boolean);
588var
589  OldColors:PFPColorArray;
590  OldPixels:PFPIntegerArray;
591  r,c:Integer;
592begin
593  if Value<>assigned(FPalette)
594  then
595    if Value
596    then
597      begin
598        FPalette:=TFPPalette.Create(0);
599        //FPalette.Add(colTransparent);
600        if assigned(FData) then
601          begin
602          OldColors:=PFPColorArray(FData);
603          GetMem(FData,FWidth*FHeight*SizeOf(Integer));
604          for r:=0 to FHeight-1 do
605            for c:=0 to FWidth-1 do
606              Colors[c,r]:=OldColors^[r*FWidth+c];
607          FreeMem(OldColors);
608          end;
609      end
610    else
611      begin
612        if Assigned(FData) then
613          begin
614          OldPixels:=PFPIntegerArray(FData);
615          GetMem(FData,FWidth*FHeight*SizeOf(TFPColor));
616          for r:=0 to FHeight-1 do
617            for c:=0 to FWidth-1 do
618              Colors[c,r]:=FPalette.Color[OldPixels^[r*FWidth+c]];
619          FreeMem(OldPixels);
620          end;
621        FPalette.Free;
622        FPalette:=nil;
623      end;
624end;
625