fpc/fcl/image/fpreadxpm.pp
fpc 790a4fe2d3 * log and id tags removed
git-svn-id: trunk@42 -
2005-05-21 09:42:41 +00:00

329 lines
7.7 KiB
ObjectPascal

{
This file is part of the Free Pascal run time library.
Copyright (c) 2003 by the Free Pascal development team
XPM reader class.
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
{$mode objfpc}{$h+}
unit FPReadXPM;
interface
uses FPImage, classes, sysutils;
type
TFPReaderXPM = class (TFPCustomImageReader)
private
width, height, ncols, cpp, xhot, yhot : integer;
xpmext : boolean;
palette : TStringList;
function HexToColor(s : string) : TFPColor;
function NameToColor(s : string) : TFPColor;
function DiminishWhiteSpace (s : string) : string;
protected
procedure InternalRead (Str:TStream; Img:TFPCustomImage); override;
function InternalCheck (Str:TStream) : boolean; override;
public
constructor Create; override;
destructor Destroy; override;
end;
implementation
const
WhiteSpace = ' '#8#10#13;
constructor TFPReaderXPM.create;
begin
inherited create;
palette := TStringList.Create;
end;
destructor TFPReaderXPM.Destroy;
begin
Palette.Free;
inherited destroy;
end;
function TFPReaderXPM.HexToColor(s : string) : TFPColor;
var l : integer;
function CharConv (c : char) : longword;
begin
if (c >= 'A') and (c <= 'F') then
result := ord (c) - ord('A') + 10
else if (c >= '0') and (c <= '9') then
result := ord (c) - ord('0')
else
raise exception.CreateFmt ('Wrong character (%s) in hexadecimal number', [c]);
end;
function convert (n : string) : word;
var t,r: integer;
begin
result := 0;
t := length(n);
if t > 4 then
raise exception.CreateFmt ('Too many bytes for color (%s)',[s]);
for r := 1 to length(n) do
result := (result shl 4) or CharConv(n[r]);
// fill missing bits
case t of
1: result:=result or (result shl 4) or (result shl 8) or (result shl 12);
2: result:=result or (result shl 8);
3: result:=result or (result shl 12);
end;
end;
begin
s := uppercase (s);
l := length(s) div 3;
result.red := (Convert(copy(s,1,l)));
result.green := (Convert(copy(s,l+1,l)));
result.blue := Convert(copy(s,l+l+1,l));
result.alpha := AlphaOpaque;
end;
function TFPReaderXPM.NameToColor(s : string) : TFPColor;
begin
s := lowercase (s);
if s = 'transparent' then
result := colTransparent
else if s = 'none' then
result := colTransparent
else if s = 'black' then
result := colBlack
else if s = 'blue' then
result := colBlue
else if s = 'green' then
result := colGreen
else if s = 'cyan' then
result := colCyan
else if s = 'red' then
result := colRed
else if s = 'magenta' then
result := colMagenta
else if s = 'yellow' then
result := colYellow
else if s = 'white' then
result := colWhite
else if s = 'gray' then
result := colGray
else if s = 'ltgray' then
result := colLtGray
else if s = 'dkblue' then
result := colDkBlue
else if s = 'dkgreen' then
result := colDkGreen
else if s = 'dkcyan' then
result := colDkCyan
else if s = 'dkred' then
result := colDkRed
else if s = 'dkmagenta' then
result := colDkMagenta
else if s = 'dkyellow' then
result := colDkYellow
else if s = 'maroon' then
result := colMaroon
else if s = 'ltgreen' then
result := colLtGreen
else if s = 'olive' then
result := colOlive
else if s = 'navy' then
result := colNavy
else if s = 'purple' then
result := colPurple
else if s = 'teal' then
result := colTeal
else if s = 'silver' then
result := colSilver
else if s = 'lime' then
result := colLime
else if s = 'fuchsia' then
result := colFuchsia
else if s = 'aqua' then
result := colAqua
else
result := colTransparent;
end;
function TFPReaderXPM.DiminishWhiteSpace (s : string) : string;
var r : integer;
Doit : boolean;
begin
Doit := true;
result := '';
for r := 1 to length(s) do
if pos(s[r],WhiteSpace)>0 then
begin
if DoIt then
result := result + ' ';
DoIt := false;
end
else
begin
DoIt := True;
result := result + s[r];
end;
end;
procedure TFPReaderXPM.InternalRead (Str:TStream; Img:TFPCustomImage);
var l : TStringList;
procedure TakeInteger (var s : string; var i : integer);
var r : integer;
begin
r := pos (' ', s);
if r = 0 then
begin
i := StrToInt(s);
s := '';
end
else
begin
i := StrToInt(copy(s,1,r-1));
delete (s, 1, r);
end;
end;
procedure ParseFirstLine;
var s : string;
begin
s := l[0];
// diminish all whitespace to 1 blank
s := DiminishWhiteSpace (trim(s));
Takeinteger (s, width);
Takeinteger (s, height);
Takeinteger (s, ncols);
Takeinteger (s, cpp);
if s <> '' then
begin
Takeinteger (s, xhot);
Takeinteger (s, yhot);
xpmext := (comparetext(s, 'XPMEXT') = 0);
if (s <> '') and not xpmext then
Raise Exception.Create ('Wrong word for XPMEXT tag');
end;
end;
procedure AddPalette (const code:string;const Acolor:TFPColor);
var r : integer;
begin
r := Palette.Add(code);
img.palette.Color[r] := Acolor;
end;
procedure AddToPalette(s : string);
var code : string;
c : TFPColor;
p : integer;
begin
code := copy(s,1,cpp);
s := trim(diminishWhiteSpace (copy(s,cpp+1,maxint)));
// Search for c-key in the color values
if s[1] = 'c' then
delete (s, 1, 2)
else
begin
p := pos (' c ',s);
if p = 0 then
s := ''
else
delete (s, 1, p+2);
end;
// c color value is first word, remove the rest of the line
p := pos(' ', s);
if p > 0 then
delete (s, p, maxint);
// check if exists
if s = '' then
raise exception.Create ('Only c-key is used for colors');
// convert #hexadecimal value to integer and place in palette
if s[1] = '#' then
c := HexToColor(copy(s,2,maxint))
else
c := NameToColor(s);
AddPalette(code,c);
end;
procedure ReadPalette;
var r : integer;
begin
Palette.Clear;
Img.Palette.Count := ncols;
for r := 1 to ncols do
AddToPalette (l[r]);
end;
procedure ReadLine (const s : string; imgindex : integer);
var color, r, p : integer;
code : string;
begin
p := 1;
for r := 1 to width do
begin
code := copy(s, p, cpp);
inc(p,cpp);
for color := 0 to Palette.Count-1 do
{ Can't use indexof, as compare must be case sensitive }
if code = Palette[color] then begin
img.pixels[r-1,imgindex] := color;
Break;
end;
end;
end;
procedure ReadData;
var r : integer;
begin
for r := 1 to height do
ReadLine (l[ncols+r], r-1);
end;
var p, r : integer;
begin
l := TStringList.Create;
try
l.LoadFromStream (Str);
for r := l.count-1 downto 0 do
begin
p := pos ('"', l[r]);
if p > 0 then
l[r] := copy(l[r], p+1, lastdelimiter('"',l[r])-p-1)
else
l.delete(r);
end;
ParseFirstLine;
Img.SetSize (width, height);
ReadPalette;
ReadData;
finally
l.Free;
end;
end;
function TFPReaderXPM.InternalCheck (Str:TStream) : boolean;
var s : string[9];
l : integer;
begin
try
l := str.Read (s[1],9);
s[0] := char(l);
if l <> 9 then
result := False
else
result := (s = '/* XPM */');
except
result := false;
end;
end;
initialization
ImageHandlers.RegisterImageReader ('XPM Format', 'xpm', TFPReaderXPM);
end.