MODULE AnimationCodec;
IMPORT
Streams, KernelLog, Strings, Files, Codecs, XML, XMLScanner, XMLParser, WMGraphics;
CONST
Version = "ANI09a";
HeaderMissing* = 20001;
HeaderError* = 20002;
WrongVersion* = 20003;
FormatError* = 20004;
ImageNotFound* = 20010;
XmlHeader = "Header";
XmlVersion = "version";
XmlWidth = "width";
XmlHeight = "height";
XmlBackgroundColor = "bgcolor";
XmlFrames = "Frames";
XmlFrame = "Frame";
XmlImageName = "image";
XmlLeft = "x";
XmlTop = "y";
XmlDelayTime = "time";
XmlDisposeMode = "mode";
XmlFrom = "from";
XmlTo = "to";
Debug = TRUE;
TYPE
Settings = RECORD
x, y : LONGINT;
time, mode : LONGINT;
END;
TYPE
Decoder* = OBJECT(Codecs.AnimationDecoder)
VAR
animation : XML.Element;
width, height, bgcolor : LONGINT;
default : Settings;
error : BOOLEAN;
PROCEDURE &Init*;
BEGIN
animation := NIL;
width := 0; height := 0; bgcolor := 0;
RestoreDefaultSettings;
error := FALSE;
END Init;
PROCEDURE RestoreDefaultSettings;
BEGIN
default.x := 0; default.y := 0; default.time := 20; default.mode := Codecs.Unspecified;
END RestoreDefaultSettings;
PROCEDURE ReportError(pos, line, col: LONGINT; CONST msg: ARRAY OF CHAR);
BEGIN
error := TRUE;
END ReportError;
PROCEDURE Open*(in : Streams.Reader; VAR res : LONGINT);
VAR
scanner : XMLScanner.Scanner; parser : XMLParser.Parser; document : XML.Document;
content : XML.Content;
PROCEDURE CheckHeader(header : XML.Element; VAR res : LONGINT);
VAR name, version : Strings.String; ignore : BOOLEAN;
BEGIN
ASSERT(header # NIL);
name := header.GetName();
IF (name # NIL) & (name^ = XmlHeader) THEN
version := header.GetAttributeValue(XmlVersion);
IF (version # NIL) THEN
IF (version^ = Version) THEN
IF GetInteger(header, XmlWidth, width) & GetInteger(header, XmlHeight, height) & (width > 0) & (height > 0) THEN
ignore := GetInteger(header, XmlBackgroundColor, bgcolor);
res := Codecs.ResOk;
ELSE
res := HeaderError;
END;
ELSE
res := WrongVersion;
END;
ELSE
res := HeaderError;
END;
ELSE
res := HeaderMissing;
END;
END CheckHeader;
BEGIN
ASSERT(in # NIL);
NEW(scanner, in);
NEW(parser, scanner);
parser.reportError := ReportError;
document := parser.Parse();
IF ~error & (document # NIL) THEN
animation := document.GetRoot();
IF (animation # NIL) THEN
content := animation.GetFirst();
IF (content # NIL) & (content IS XML.Element) THEN
CheckHeader(content(XML.Element), res);
IF (res # Codecs.ResOk) THEN animation := NIL; END;
ELSE
res := Codecs.ResFailed;
END;
ELSE
res := Codecs.ResFailed;
END;
ELSE
animation := NIL;
res := Codecs.ResFailed;
END;
IF Debug & (res # Codecs.ResOk) THEN
KernelLog.String("AnimationCodec: Could not open animation, res = "); KernelLog.Int(res, 0); KernelLog.Ln;
END;
END Open;
PROCEDURE ProcessFrame(frame : XML.Element; VAR desc : Codecs.ImageDescriptor; VAR res : LONGINT);
VAR string, imageName : Strings.String; from, to, current, minDigits : LONGINT; filename : Files.FileName; last, d : Codecs.ImageDescriptor;
BEGIN
NEW(desc);
IF ~GetInteger(frame, XmlLeft, desc.left) THEN desc.left := default.x; END;
IF ~GetInteger(frame, XmlTop, desc.top) THEN desc.top := default.y; END;
IF ~GetInteger(frame, XmlDelayTime, desc.delayTime) THEN desc.delayTime := default.time; END;
IF ~GetInteger(frame, XmlDisposeMode, desc.disposeMode) THEN desc.disposeMode := default.mode; END;
IF ~GetInteger(frame, XmlFrom, from) THEN from := 0 END;
IF ~GetInteger(frame, XmlTo, to) THEN to := 0; END;
imageName := frame.GetAttributeValue(XmlImageName);
IF (imageName # NIL) THEN
IF (from = 0) & (to = 0) THEN
desc.image := WMGraphics.LoadImage(imageName^, TRUE);
IF (desc.image # NIL) THEN
res := Codecs.ResOk;
ELSE
res := ImageNotFound;
END;
ELSE
string := frame.GetAttributeValue(XmlFrom);
IF (string # NIL) THEN
Strings.TrimWS(string^);
minDigits := Strings.Length(string^);
ELSE
minDigits := 0;
END;
last := NIL; d := desc;
res := Codecs.ResOk;
current := from;
REPEAT
GenerateFilename(imageName^, filename, current, minDigits);
d.image := WMGraphics.LoadImage(filename, TRUE);
IF (d.image = NIL) THEN
res := ImageNotFound;
END;
IF (last = NIL) THEN
last := d;
ELSE
d.previous := last;
last.next := d;
last := d;
END;
INC(current);
IF (current <= to) THEN
NEW(d);
d.left := last.left; d.top := last.top; d.delayTime := last.delayTime; d.disposeMode := last.disposeMode;
END;
UNTIL (res # Codecs.ResOk) OR (current > to);
END;
ELSE
res := FormatError;
END;
END ProcessFrame;
PROCEDURE ProcessFrames(frames : XML.Element; VAR sequence : Codecs.ImageSequence; VAR res : LONGINT);
VAR frame : XML.Content; name : Strings.String; desc, last : Codecs.ImageDescriptor; value : LONGINT;
BEGIN
ASSERT(frames # NIL);
last := sequence.images;
IF (last # NIL) THEN WHILE (last.next # NIL) DO last := last.next; END; END;
RestoreDefaultSettings;
IF GetInteger(frames, XmlLeft, value) THEN default.x := value; END;
IF GetInteger(frames, XmlTop, value) THEN default.y := value; END;
IF GetInteger(frames, XmlDelayTime, value) THEN default.time := value; END;
IF GetInteger(frames, XmlDisposeMode,value) THEN default.mode := value; END;
res := Codecs.ResOk;
frame := frames.GetFirst();
WHILE (res = Codecs.ResOk) & (frame # NIL) DO
IF (frame IS XML.Element) THEN
name := frame(XML.Element).GetName();
IF (name # NIL) & (name^ = XmlFrame) THEN
ProcessFrame(frame(XML.Element), desc, res);
IF (res = Codecs.ResOk) THEN
IF (last = NIL) THEN
ASSERT(sequence.images = NIL);
sequence.images := desc;
ELSE
desc.previous := last;
last.next := desc;
END;
last := desc;
ELSE
sequence.images := NIL;
END;
END;
END;
frame := frames.GetNext(frame);
END;
END ProcessFrames;
PROCEDURE GetImageSequence*(VAR sequence : Codecs.ImageSequence; VAR res : LONGINT);
VAR content : XML.Content; frames : XML.Element; string : Strings.String;
BEGIN
IF (animation = NIL) THEN res := Codecs.ResFailed; RETURN; END;
sequence.width := width; sequence.height := height; sequence.bgColor := bgcolor; sequence.images := NIL;
res := Codecs.ResOk;
content := animation.GetFirst();
WHILE (res = Codecs.ResOk) & (content # NIL) DO
IF (content IS XML.Element) THEN
frames := content(XML.Element);
string := frames.GetName();
IF (string # NIL) & (string^ = XmlFrames) THEN
ProcessFrames(frames, sequence, res);
IF (res # Codecs.ResOk) THEN
sequence.images := NIL;
END;
END;
END;
content := animation.GetNext(content);
END;
IF Debug & (res # Codecs.ResOk) THEN
KernelLog.String("AnimationCodec: Could not decode image sequence, res = "); KernelLog.Int(res, 0); KernelLog.Ln;
END;
END GetImageSequence;
END Decoder;
PROCEDURE GenerateFilename(CONST base : ARRAY OF CHAR; VAR filename : ARRAY OF CHAR; suffix, minDigits : LONGINT);
VAR name, extension : Files.FileName; temp, digits : LONGINT; nbr : ARRAY 32 OF CHAR;
BEGIN
Files.SplitExtension(base, name, extension);
COPY(name, filename);
digits := 0; temp := suffix;
REPEAT
INC(digits); temp := temp DIV 10;
UNTIL temp = 0;
WHILE (digits < minDigits) DO Strings.Append(filename, "0"); INC(digits); END;
Strings.IntToStr(suffix, nbr);
Strings.Append(filename, nbr);
Strings.Append(filename, ".");
Strings.Append(filename, extension);
KernelLog.String(filename); KernelLog.Ln;
END GenerateFilename;
PROCEDURE GetInteger(element : XML.Element; CONST attributeName : ARRAY OF CHAR; VAR value : LONGINT) : BOOLEAN;
VAR valueStr : Strings.String;
BEGIN
ASSERT(element # NIL);
value := 0;
valueStr := element.GetAttributeValue(attributeName);
IF (valueStr # NIL) THEN
Strings.TrimWS(valueStr^);
Strings.StrToInt(valueStr^, value);
RETURN TRUE;
ELSE
RETURN FALSE;
END;
END GetInteger;
PROCEDURE GenDecoder*() : Codecs.AnimationDecoder;
VAR d : Decoder;
BEGIN
NEW(d); RETURN d;
END GenDecoder;
END AnimationCodec.