#include "pch.h"
class FrameImage : public Image {
private:
TRef<Image> m_pimageBackground;
TRef<Surface> m_psurface;
TRef<IObject> m_pobjectMemory;
int m_nFrame;
int m_frameCurrent;
DWORD* m_pdwOffsets;
BYTE* m_prle;
public:
FrameImage(
Image* pimageBackground,
Number* pframe,
int nFrames,
DWORD* pdwOffsets,
BYTE* prle,
IObject* pobjectMemory
) :
Image(pframe, pimageBackground),
m_pimageBackground(pimageBackground),
m_nFrame(nFrames),
m_pdwOffsets(pdwOffsets),
m_prle(prle),
m_pobjectMemory(pobjectMemory)
{
TRef<Surface> psurfaceBackground = m_pimageBackground->GetSurface();
m_psurface =
psurfaceBackground->CreateCompatibleSurface(
psurfaceBackground->GetSize(),
SurfaceType2D()
);
m_psurface->BitBlt(WinPoint(0, 0), psurfaceBackground);
m_frameCurrent = 0;
m_bounds.SetRect(
Rect(
Point(0, 0),
Point::Cast(m_psurface->GetSize())
)
);
}
~FrameImage()
{
if (m_pobjectMemory == NULL) {
delete[] m_pdwOffsets;
delete[] m_prle;
}
}
Number* GetFrameNumber() { return Number::Cast(GetChild(0)); }
int GetFrame()
{
return
bound(
(int)(GetFrameNumber()->GetValue() * m_nFrame),
0,
m_nFrame - 1
);
}
void PlayRLE(BYTE* pd, BYTE* prle, BYTE* pend)
{
while (prle < pend) {
WORD word = *(WORD*)prle;
int length = word & RLELengthMask;
prle += 2;
switch (word & RLEMask) {
case RLEMaskFill:
{
for (int index = length; index > 0; index --) {
(*(BYTE*)pd) = (*(BYTE*)pd) ^ (*(BYTE*)prle);
pd += 1;
prle += 1;
}
}
break;
case RLEMaskBYTE:
{
BYTE byte = *(BYTE*)prle;
prle += 1;
for (int index = length; index > 0; index --) {
(*(BYTE*)pd) = (*(BYTE*)pd) ^ byte;
pd += 1;
}
}
break;
case RLEMaskWORD:
{
WORD word = *(WORD*)prle;
prle += 2;
for (int index = length; index > 0; index --) {
(*(WORD*)pd) = (*(WORD*)pd) ^ word;
pd += 2;
}
}
break;
case RLEMaskDWORD:
{
DWORD dword = *(DWORD*)prle;
prle += 4;
for (int index = length; index > 0; index --) {
(*(DWORD*)pd) = (*(DWORD*)pd) ^ dword;
pd += 4;
}
}
break;
}
}
}
void UpdateFrame()
{
int newFrame = GetFrame();
if (m_frameCurrent != newFrame) {
BYTE* pd = m_psurface->GetWritablePointer();
while (m_frameCurrent < newFrame) {
PlayRLE(
pd,
m_prle + m_pdwOffsets[m_frameCurrent],
m_prle + m_pdwOffsets[m_frameCurrent + 1]
);
m_frameCurrent++;
}
while (m_frameCurrent > newFrame) {
m_frameCurrent--;
PlayRLE(
pd,
m_prle + m_pdwOffsets[m_frameCurrent],
m_prle + m_pdwOffsets[m_frameCurrent + 1]
);
}
m_psurface->ReleasePointer();
}
}
TRef<Surface> GetSurface()
{
UpdateFrame();
return m_psurface;
}
void Render(Context* pcontext)
{
UpdateFrame();
pcontext->DrawImage(m_psurface);
}
ZString GetFunctionName()
{
return "FrameImage";
}
void Write(IMDLBinaryFile* pmdlFile)
{
WriteChildren(pmdlFile);
pmdlFile->WriteReference("FrameImage");
TRef<ZFile> pfile = pmdlFile->WriteBinary();
pfile->Write((void*)&m_nFrame, sizeof(m_nFrame) );
pfile->Write((void*)m_pdwOffsets, sizeof(DWORD) * (m_nFrame));
pfile->Write((void*)m_prle, m_pdwOffsets[m_nFrame - 1]);
pfile->WritePad(m_pdwOffsets[m_nFrame - 1]);
}
};
class FrameImageFactory : public IFunction {
public:
FrameImageFactory()
{
}
TRef<IObject> Apply(ObjectStack& stack)
{
ZUnimplemented();
return NULL;
}
TRef<IObject> Read(IBinaryReaderSite* psite, ObjectStack& stack)
{
Number* pframe = Number::Cast((IObject*)stack.Pop());
Image* pimage = Image::Cast((Value*)(IObject*)stack.Pop());
DWORD nFrame = psite->GetDWORD();
DWORD* pdwOffsets = (DWORD*)psite->GetPointer();
BYTE* prle = (BYTE*)(pdwOffsets + nFrame);
psite->MovePointer(
sizeof(DWORD) * nFrame
+ pdwOffsets[nFrame - 1]
);
return
(Value*)new FrameImage(
pimage,
pframe,
nFrame,
pdwOffsets,
prle,
psite->GetMemoryObject()
);
}
};
TRef<IFunction> CreateFrameImageFactory()
{
return new FrameImageFactory();
}
class CompressAnimClass {
private:
BYTE* m_pdata;
int m_sizeData;
WinPoint m_sizeFrame;
TRef<Surface> m_psurfaceFile;
WinPoint m_sizeFile ;
int m_xframes ;
int m_yframes ;
int m_nframes ;
int m_xsize ;
int m_ysize ;
public:
CompressAnimClass(
Surface* psurfaceFile,
int xframes,
int yframes,
TRef<Surface>& psurfaceBackground,
DWORD*& pdwOffsets,
BYTE*& prleStart
) {
m_xframes = xframes;
m_yframes = yframes;
m_nframes = m_xframes * m_yframes;
m_psurfaceFile = psurfaceFile;
m_sizeFile = m_psurfaceFile->GetSize();
m_xsize = m_sizeFile.X() / xframes;
m_ysize = m_sizeFile.Y() / yframes;
m_sizeFrame = WinPoint(m_xsize, m_ysize);
psurfaceBackground = m_psurfaceFile->CreateCompatibleSurface(m_sizeFrame, SurfaceType2D());
GetFrame(psurfaceBackground, 0);
TRef<Surface> psurface0 = m_psurfaceFile->CreateCompatibleSurface(m_sizeFrame, SurfaceType2D());
TRef<Surface> psurface1 = m_psurfaceFile->CreateCompatibleSurface(m_sizeFrame, SurfaceType2D());
int length = psurface0->GetPitch() * m_ysize;
int size = (length + 2) * m_nframes;
prleStart = new BYTE[size];
pdwOffsets = new DWORD[m_nframes];
pdwOffsets[0] = 0;
BYTE* pd = new BYTE[length];
BYTE* prle = prleStart;
for (int frame = 0; frame < m_nframes - 1; frame++) {
GetFrame(psurface0, frame );
GetFrame(psurface1, frame + 1);
const BYTE* p0 = psurface0->GetPointer();
const BYTE* p1 = psurface1->GetPointer();
DoXOR(p0, p1, pd, length);
DoRLE(pd, length, prle);
pdwOffsets[frame + 1 ] = prle - prleStart;
}
ZAssert(prle - prleStart <= size);
delete pd;
}
void GetFrame(Surface* psurface, int frame)
{
int x = (frame % m_xframes) * m_xsize;
int y = (frame / m_xframes) * m_ysize;
psurface->BitBlt(
WinPoint(0, 0),
m_psurfaceFile,
WinRect(x, y, x + m_xsize, y + m_ysize)
);
}
void DoXOR(const BYTE* p0, const BYTE* p1, BYTE* pd, int length)
{
for (int index = 0; index < length; index++) {
pd[index] = p0[index] ^ p1[index];
}
}
template<class Type>
void DoStoreSpan(Type** ppd, BYTE*& prle, WORD code, WORD count)
{
*(WORD*)prle = code | count;
*(Type*)(prle + 2) = (*ppd)[0];
(*ppd) += count;
prle += sizeof(Type) + 2;
}
template<class Type>
WORD CouldStoreSpan(Type** ppd, Type* pend)
{
if ((*ppd) + 1 < pend) {
Type value = (*ppd)[0];
WORD count = 0;
while (
((*ppd) + count + 1 < pend)
&& (*ppd)[count] == value
) {
count++;
}
if (count * sizeof(Type) > 2 + sizeof(Type)) {
return min(count, RLELengthMask);
}
}
return 0;
}
void DoRawStoreSpan(BYTE* pstart, BYTE*& pd, BYTE*&prle)
{
WORD length = pd - pstart;
if (length > 0) {
*(WORD*)prle = RLEMaskFill | length;
memcpy(prle + 2, pstart, length);
prle += 2 + length;
}
}
void DoRLE(BYTE* pd, int length, BYTE*& prle)
{
BYTE* pend = pd + length;
BYTE* pstart = pd;
while (pd < pend) {
WORD count;
if (count = CouldStoreSpan((DWORD**)&pd, (DWORD*)pend)) {
DoRawStoreSpan(pstart, pd, prle);
DoStoreSpan((DWORD**)&pd, prle, RLEMaskDWORD, count);
pstart = pd;
} else if (count = CouldStoreSpan((WORD**)&pd, (WORD*)pend)) {
DoRawStoreSpan(pstart, pd, prle);
DoStoreSpan((WORD**)&pd, prle, RLEMaskWORD, count);
pstart = pd;
} else if (count = CouldStoreSpan((BYTE**)&pd, (BYTE*)pend)) {
DoRawStoreSpan(pstart, pd, prle);
DoStoreSpan((BYTE**)&pd, prle, RLEMaskBYTE, count);
pstart = pd;
}
pd++;
}
DoRawStoreSpan(pstart, pd, prle);
}
};
TRef<Image> CreateFrameImage(
Number* pframe,
Surface* psurface,
int xframes,
int yframes
) {
TRef<Surface> psurfaceBackground;
DWORD* pdwOffsets;
BYTE* prle;
CompressAnimClass foo(
psurface,
xframes,
yframes,
psurfaceBackground,
pdwOffsets,
prle
);
return
new FrameImage(
new ConstantImage(psurfaceBackground, ZString()),
pframe,
xframes * yframes,
pdwOffsets,
prle,
NULL
);
}
class FrameImageButtonPaneImpl :
public FrameImageButtonPane,
public Value
{
TRef<ModifiableNumber> m_pframe;
TRef<Image> m_pimage;
TRef<EventSourceImpl> m_peventSource;
TRef<EventSourceImpl> m_peventMouseEnterSource;
TRef<EventSourceImpl> m_peventMouseLeaveSource;
bool m_bInside;
float m_duration;
float m_valueStart;
float m_timeStart;
float m_loop;
float m_finish;
public:
FrameImageButtonPaneImpl(
Number* ptime,
Image* pimage,
ModifiableNumber* pframe,
float duration,
bool loop,
bool finish
) :
Value(ptime),
m_peventSource(new EventSourceImpl()),
m_peventMouseEnterSource(new EventSourceImpl()),
m_peventMouseLeaveSource(new EventSourceImpl()),
m_pimage(pimage),
m_pframe(pframe),
m_duration(duration),
m_loop(loop),
m_finish(finish),
m_bInside(false),
m_valueStart(0),
m_timeStart(ptime->GetValue())
{
InternalSetSize(
WinPoint::Cast(m_pimage->GetBounds().GetRect().Size())
);
}
Number* GetTime() { return Number::Cast(GetChild(0)); }
IEventSource* GetEventSource()
{
return m_peventSource;
}
IEventSource* GetMouseEnterEventSource()
{
return m_peventMouseEnterSource;
}
IEventSource* GetMouseLeaveEventSource()
{
return m_peventMouseLeaveSource;
}
void ChildChanged(Value* pvalue, Value* pvalueNew)
{
Value::ChildChanged(pvalue, pvalueNew);
NeedPaint();
}
void Evaluate()
{
float time = GetTime()->GetValue();
float value;
float delta = (time - m_timeStart) / m_duration;
if (m_bInside) {
value = m_valueStart + delta;
if (m_loop)
{
value = float(value - (int)value);
}
} else {
if (m_finish)
{
value = m_valueStart + delta;
if (value > 1.0)
value = 0.0;
}
else if (m_loop)
{
value = m_valueStart;
}
else
{
value = m_valueStart - delta;
}
}
value = bound(value, 0.0f, 1.0f);
if (value != m_pframe->GetValue()) {
m_pframe->SetValue(value);
NeedPaint();
}
}
void Paint(Surface* psurface)
{
Update();
psurface->BitBlt(WinPoint(0, 0), m_pimage->GetSurface());
}
void MouseEnter(IInputProvider* pprovider, const Point& point)
{
m_bInside = true;
m_valueStart = m_pframe->GetValue();
m_timeStart = GetTime()->GetValue();
m_peventMouseEnterSource->Trigger();
}
void MouseLeave(IInputProvider* pprovider)
{
m_bInside = false;
m_valueStart = m_pframe->GetValue();
m_timeStart = GetTime()->GetValue();
m_peventMouseLeaveSource->Trigger();
}
MouseResult Button(IInputProvider* pprovider, const Point& point, int button, bool bCaptured, bool bInside, bool bDown)
{
if (button == 0) {
if (bDown) {
m_peventSource->Trigger();
}
}
return MouseResult();
}
};
TRef<FrameImageButtonPane> CreateFrameImageButtonPane(
Number* ptime,
Image* pimage,
ModifiableNumber* pframe,
float duration,
bool loop,
bool finish
) {
return new FrameImageButtonPaneImpl(ptime, pimage, pframe, duration, loop, finish);
}