mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-12-14 12:09:36 +00:00
Cleanup, add scanline mask builder
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#include "DisplayDeviceManager.h"
|
||||
|
||||
#include "HostDisplayDriver.h"
|
||||
#include "PLQuickdraw.h"
|
||||
#include "PLQDraw.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "QDStandardPalette.h"
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace PortabilityLayer
|
||||
{
|
||||
public:
|
||||
virtual uint32_t GetTime() const = 0;
|
||||
virtual void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const = 0;
|
||||
virtual HostMutex *CreateMutex() = 0;
|
||||
virtual HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) = 0;
|
||||
|
||||
|
||||
14
PortabilityLayer/IPlotter.h
Normal file
14
PortabilityLayer/IPlotter.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "PlotDirection.h"
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
struct Vec2i;
|
||||
|
||||
struct IPlotter
|
||||
{
|
||||
virtual PlotDirection PlotNext() = 0;
|
||||
virtual const Vec2i &GetPoint() const = 0;
|
||||
};
|
||||
}
|
||||
101
PortabilityLayer/LinePlotter.cpp
Normal file
101
PortabilityLayer/LinePlotter.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "LinePlotter.h"
|
||||
#include "Vec2i.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
LinePlotter::LinePlotter()
|
||||
: m_point(0, 0)
|
||||
, m_endPoint(0, 0)
|
||||
, m_dx(0)
|
||||
, m_dy(0)
|
||||
, m_err(0)
|
||||
, m_xIncrPos(true)
|
||||
, m_yIncrPos(true)
|
||||
, m_xMove(PlotDirection_Exhausted)
|
||||
, m_yMove(PlotDirection_Exhausted)
|
||||
, m_xyMove(PlotDirection_Exhausted)
|
||||
{
|
||||
}
|
||||
|
||||
PlotDirection LinePlotter::PlotNext()
|
||||
{
|
||||
if (m_point == m_endPoint)
|
||||
return PlotDirection_Exhausted;
|
||||
|
||||
bool movedX = false;
|
||||
const int32_t err2 = 2 * m_err;
|
||||
if (err2 >= m_dy)
|
||||
{
|
||||
m_err += m_dy;
|
||||
m_point.m_x += m_xIncrPos ? 1 : -1;
|
||||
movedX = true;
|
||||
}
|
||||
|
||||
if (err2 <= m_dx)
|
||||
{
|
||||
m_err += m_dx;
|
||||
m_point.m_y += m_yIncrPos ? 1 : -1;
|
||||
|
||||
if (movedX)
|
||||
return m_xyMove;
|
||||
else
|
||||
return m_yMove;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(movedX);
|
||||
return m_xMove;
|
||||
}
|
||||
}
|
||||
|
||||
const Vec2i &LinePlotter::GetPoint() const
|
||||
{
|
||||
return m_point;
|
||||
}
|
||||
|
||||
void LinePlotter::Reset(const Vec2i &pointA, const Vec2i &pointB)
|
||||
{
|
||||
m_dx = pointB.m_x - pointA.m_x;
|
||||
if (m_dx < 0)
|
||||
m_dx = -m_dx;
|
||||
|
||||
m_dy = pointB.m_y - pointA.m_y;
|
||||
if (m_dy > 0)
|
||||
m_dy = -m_dy;
|
||||
|
||||
m_xIncrPos = (pointA.m_x < pointB.m_x);
|
||||
m_yIncrPos = (pointA.m_y < pointB.m_y);
|
||||
|
||||
m_err = m_dx + m_dy;
|
||||
|
||||
m_point = pointA;
|
||||
m_endPoint = pointB;
|
||||
|
||||
if (m_xIncrPos)
|
||||
{
|
||||
if (m_yIncrPos)
|
||||
m_xyMove = PlotDirection_PosX_PosY;
|
||||
else
|
||||
m_xyMove = PlotDirection_PosX_NegY;
|
||||
|
||||
m_xMove = PlotDirection_PosX_0Y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_yIncrPos)
|
||||
m_xyMove = PlotDirection_NegX_PosY;
|
||||
else
|
||||
m_xyMove = PlotDirection_NegX_NegY;
|
||||
|
||||
m_xMove = PlotDirection_NegX_0Y;
|
||||
}
|
||||
|
||||
|
||||
if (m_yIncrPos)
|
||||
m_yMove = PlotDirection_0X_PosY;
|
||||
else
|
||||
m_yMove = PlotDirection_0X_NegY;
|
||||
}
|
||||
}
|
||||
30
PortabilityLayer/LinePlotter.h
Normal file
30
PortabilityLayer/LinePlotter.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "PlotDirection.h"
|
||||
#include "Vec2i.h"
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
class LinePlotter
|
||||
{
|
||||
public:
|
||||
LinePlotter();
|
||||
PlotDirection PlotNext();
|
||||
const Vec2i &GetPoint() const;
|
||||
|
||||
void Reset(const Vec2i &pointA, const Vec2i &pointB);
|
||||
|
||||
private:
|
||||
Vec2i m_point;
|
||||
Vec2i m_endPoint;
|
||||
int32_t m_dx;
|
||||
int32_t m_dy;
|
||||
int32_t m_err;
|
||||
bool m_xIncrPos;
|
||||
bool m_yIncrPos;
|
||||
|
||||
PlotDirection m_xyMove;
|
||||
PlotDirection m_xMove;
|
||||
PlotDirection m_yMove;
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "PLApplication.h"
|
||||
#include "PLPasStr.h"
|
||||
#include "PLKeyEncoding.h"
|
||||
#include "PLQuickdraw.h"
|
||||
#include "PLQDraw.h"
|
||||
|
||||
#include "AEManager.h"
|
||||
#include "DisplayDeviceManager.h"
|
||||
@@ -901,10 +901,17 @@ void GetDateTime(UInt32 *dateTime)
|
||||
|
||||
void GetTime(DateTimeRec *dateTime)
|
||||
{
|
||||
PL_NotYetImplemented_TODO("DateTime");
|
||||
dateTime->month = 1;
|
||||
dateTime->hour = 0;
|
||||
dateTime->minute = 0;
|
||||
unsigned int year;
|
||||
unsigned int month;
|
||||
unsigned int day;
|
||||
unsigned int hour;
|
||||
unsigned int minute;
|
||||
unsigned int second;
|
||||
PortabilityLayer::HostSystemServices::GetInstance()->GetLocalDateTime(year, month, day, hour, minute, second);
|
||||
|
||||
dateTime->month = month;
|
||||
dateTime->hour = hour;
|
||||
dateTime->minute = minute;
|
||||
}
|
||||
|
||||
UInt32 GetDblTime()
|
||||
@@ -955,22 +962,6 @@ long GetHandleSize(Handle handle)
|
||||
return static_cast<long>(block->m_size);
|
||||
}
|
||||
|
||||
void HNoPurge(Handle hdl)
|
||||
{
|
||||
}
|
||||
|
||||
void MoveHHi(Handle hdl)
|
||||
{
|
||||
}
|
||||
|
||||
void HLock(Handle hdl)
|
||||
{
|
||||
}
|
||||
|
||||
void HUnlock(Handle hdl)
|
||||
{
|
||||
}
|
||||
|
||||
OSErr PtrAndHand(const void *data, Handle handle, Size size)
|
||||
{
|
||||
PL_NotYetImplemented();
|
||||
|
||||
@@ -369,11 +369,6 @@ Handle NewHandle(Size size);
|
||||
void DisposeHandle(Handle handle);
|
||||
long GetHandleSize(Handle handle);
|
||||
|
||||
void HNoPurge(Handle hdl); // Marks a handle as not purgeable
|
||||
void MoveHHi(Handle hdl); // Relocates a block to the top of the heap
|
||||
void HLock(Handle hdl); // Disable relocation
|
||||
void HUnlock(Handle hdl); // Re-enable relocation
|
||||
|
||||
OSErr PtrAndHand(const void *data, Handle handle, Size size); // Appends data to the end of a handle
|
||||
void SetHandleSize(Handle hdl, Size newSize);
|
||||
|
||||
@@ -384,9 +379,6 @@ void DisposePtr(void *ptr);
|
||||
Size MaxMem(Size *growBytes);
|
||||
void PurgeSpace(long *totalFree, long *contiguousFree);
|
||||
|
||||
void HSetState(Handle handle, char state);
|
||||
char HGetState(Handle handle);
|
||||
|
||||
OSErr MemError();
|
||||
|
||||
void BlockMove(const void *src, void *dest, Size size);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#define __PL_QDOFFSCREEN_H__
|
||||
|
||||
#include "PLCore.h"
|
||||
#include "PLQuickdraw.h"
|
||||
#include "PLQDraw.h"
|
||||
|
||||
struct ColorTable
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "PLQuickdraw.h"
|
||||
#include "PLQDraw.h"
|
||||
#include "QDManager.h"
|
||||
#include "QDState.h"
|
||||
#include "DisplayDeviceManager.h"
|
||||
20
PortabilityLayer/PlotDirection.h
Normal file
20
PortabilityLayer/PlotDirection.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
enum PlotDirection
|
||||
{
|
||||
PlotDirection_NegX_NegY,
|
||||
PlotDirection_0X_NegY,
|
||||
PlotDirection_PosX_NegY,
|
||||
|
||||
PlotDirection_NegX_PosY,
|
||||
PlotDirection_0X_PosY,
|
||||
PlotDirection_PosX_PosY,
|
||||
|
||||
PlotDirection_NegX_0Y,
|
||||
PlotDirection_PosX_0Y,
|
||||
|
||||
PlotDirection_Exhausted,
|
||||
};
|
||||
}
|
||||
@@ -163,6 +163,8 @@
|
||||
<ClInclude Include="HostVOSEventQueue.h" />
|
||||
<ClInclude Include="InputManager.h" />
|
||||
<ClInclude Include="IOStream.h" />
|
||||
<ClInclude Include="IPlotter.h" />
|
||||
<ClInclude Include="LinePlotter.h" />
|
||||
<ClInclude Include="MacBinary2.h" />
|
||||
<ClInclude Include="MacFileMem.h" />
|
||||
<ClInclude Include="MacFileInfo.h" />
|
||||
@@ -194,6 +196,9 @@
|
||||
<ClInclude Include="PLLowMem.h" />
|
||||
<ClInclude Include="PLMacTypes.h" />
|
||||
<ClInclude Include="MenuManager.h" />
|
||||
<ClInclude Include="PlotDirection.h" />
|
||||
<ClInclude Include="ScanlineMaskBuilder.h" />
|
||||
<ClInclude Include="ScanlineMaskConverter.h" />
|
||||
<ClInclude Include="QDGraf.h" />
|
||||
<ClInclude Include="QDManager.h" />
|
||||
<ClInclude Include="QDPictEmitContext.h" />
|
||||
@@ -221,7 +226,7 @@
|
||||
<ClInclude Include="PLPasStr.h" />
|
||||
<ClInclude Include="PLQDOffscreen.h" />
|
||||
<ClInclude Include="PLMovies.h" />
|
||||
<ClInclude Include="PLQuickdraw.h" />
|
||||
<ClInclude Include="PLQDraw.h" />
|
||||
<ClInclude Include="ResourceManager.h" />
|
||||
<ClInclude Include="PLResources.h" />
|
||||
<ClInclude Include="PLScript.h" />
|
||||
@@ -265,6 +270,7 @@
|
||||
<ClCompile Include="HostSystemServices.cpp" />
|
||||
<ClCompile Include="HostVOSEventQueue.cpp" />
|
||||
<ClCompile Include="InputManager.cpp" />
|
||||
<ClCompile Include="LinePlotter.cpp" />
|
||||
<ClCompile Include="MacBinary2.cpp" />
|
||||
<ClCompile Include="MacFileInfo.cpp" />
|
||||
<ClCompile Include="MacFileMem.cpp" />
|
||||
@@ -286,11 +292,14 @@
|
||||
<ClCompile Include="PLNavigation.cpp" />
|
||||
<ClCompile Include="PLNumberFormatting.cpp" />
|
||||
<ClCompile Include="PLQDOffscreen.cpp" />
|
||||
<ClCompile Include="PLQuickdraw.cpp" />
|
||||
<ClCompile Include="PLQDraw.cpp" />
|
||||
<ClCompile Include="PLResourceManager.cpp" />
|
||||
<ClCompile Include="PLResources.cpp" />
|
||||
<ClCompile Include="PLSound.cpp" />
|
||||
<ClCompile Include="PLStringCompare.cpp" />
|
||||
<ClCompile Include="ScanlineMask.cpp" />
|
||||
<ClCompile Include="ScanlineMaskBuilder.cpp" />
|
||||
<ClCompile Include="ScanlineMaskConverter.cpp" />
|
||||
<ClCompile Include="QDGraf.cpp" />
|
||||
<ClCompile Include="QDManager.cpp" />
|
||||
<ClCompile Include="QDPictDecoder.cpp" />
|
||||
|
||||
@@ -90,9 +90,6 @@
|
||||
<ClInclude Include="PLMovies.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PLQuickdraw.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PLMacTypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -369,6 +366,24 @@
|
||||
<ClInclude Include="..\GpCommon\GpBitfield.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LinePlotter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PlotDirection.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PLQDraw.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ScanlineMaskConverter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="IPlotter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ScanlineMaskBuilder.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CFileStream.cpp">
|
||||
@@ -398,9 +413,6 @@
|
||||
<ClCompile Include="PLResources.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PLQuickdraw.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PLSound.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -554,5 +566,20 @@
|
||||
<ClCompile Include="..\stb\stb_image_write.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ScanlineMaskConverter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PLQDraw.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LinePlotter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ScanlineMaskBuilder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ScanlineMask.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "PLBigEndian.h"
|
||||
#include "PLQuickdraw.h"
|
||||
#include "PLQDraw.h"
|
||||
#include "GpPixelFormat.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "QDState.h"
|
||||
|
||||
#include "PLQuickdraw.h"
|
||||
#include "QDStandardPalette.h"
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
#include "PLQDraw.h"
|
||||
#include "QDStandardPalette.h"
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
QDState::QDState()
|
||||
: m_fontID(applFont)
|
||||
, m_textSize(12)
|
||||
@@ -19,10 +19,10 @@ namespace PortabilityLayer
|
||||
, m_isBackResolved8(false)
|
||||
, m_clipRegion(nullptr)
|
||||
{
|
||||
m_backUnresolvedColor.r = m_backUnresolvedColor.g = m_backUnresolvedColor.b = m_backUnresolvedColor.a = 255;
|
||||
m_backUnresolvedColor.r = m_backUnresolvedColor.g = m_backUnresolvedColor.b = m_backUnresolvedColor.a = 255;
|
||||
m_foreUnresolvedColor.r = m_foreUnresolvedColor.g = m_foreUnresolvedColor.b = 0;
|
||||
m_foreUnresolvedColor.a = 255;
|
||||
m_penPos.h = m_penPos.v = 0;
|
||||
m_penPos.h = m_penPos.v = 0;
|
||||
}
|
||||
|
||||
void QDState::SetForeColor(const RGBAColor &color)
|
||||
@@ -77,6 +77,6 @@ namespace PortabilityLayer
|
||||
cached = resolvedColor;
|
||||
|
||||
return resolvedColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
80
PortabilityLayer/ScanlineMask.cpp
Normal file
80
PortabilityLayer/ScanlineMask.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "CoreDefs.h"
|
||||
#include "ScanlineMask.h"
|
||||
#include "ScanlineMaskBuilder.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <new>
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
void ScanlineMask::Destroy()
|
||||
{
|
||||
this->~ScanlineMask();
|
||||
free(this);
|
||||
}
|
||||
|
||||
ScanlineMask *ScanlineMask::Create(const ScanlineMaskBuilder &builder)
|
||||
{
|
||||
size_t alignedPrefixSize = sizeof(ScanlineMask) + PL_SYSTEM_MEMORY_ALIGNMENT - 1;
|
||||
alignedPrefixSize -= alignedPrefixSize % PL_SYSTEM_MEMORY_ALIGNMENT;
|
||||
|
||||
const size_t longestSpan = builder.GetLongestSpan();
|
||||
const size_t numSpans = builder.GetNumSpans();
|
||||
const size_t *spans = builder.GetSpans();
|
||||
|
||||
size_t storageSize = numSpans;
|
||||
DataStorage dataStorage = DataStorage_UInt8;
|
||||
if (longestSpan <= 0xff)
|
||||
dataStorage = DataStorage_UInt8;
|
||||
else if (longestSpan <= 0xffff)
|
||||
{
|
||||
storageSize *= 2;
|
||||
dataStorage = DataStorage_UInt16;
|
||||
}
|
||||
else if (longestSpan <= 0xffffffff)
|
||||
{
|
||||
storageSize *= 4;
|
||||
dataStorage = DataStorage_UInt32;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
|
||||
void *storage = malloc(alignedPrefixSize + storageSize);
|
||||
if (!storage)
|
||||
return nullptr;
|
||||
|
||||
void *spanStorage = static_cast<uint8_t*>(storage) + alignedPrefixSize;
|
||||
|
||||
ScanlineMask *mask = new (storage) ScanlineMask(dataStorage, spanStorage, numSpans);
|
||||
|
||||
for (size_t i = 0; i < numSpans; i++)
|
||||
{
|
||||
switch (dataStorage)
|
||||
{
|
||||
case DataStorage_UInt8:
|
||||
static_cast<uint8_t*>(spanStorage)[i] = static_cast<uint8_t>(spans[i]);
|
||||
break;
|
||||
case DataStorage_UInt16:
|
||||
static_cast<uint16_t*>(spanStorage)[i] = static_cast<uint16_t>(spans[i]);
|
||||
break;
|
||||
case DataStorage_UInt32:
|
||||
static_cast<uint32_t*>(spanStorage)[i] = static_cast<uint32_t>(spans[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
ScanlineMask::ScanlineMask(DataStorage dataStorage, const void *data, size_t numSpans)
|
||||
: m_dataStorage(dataStorage)
|
||||
, m_data(data)
|
||||
, m_numSpans(numSpans)
|
||||
{
|
||||
}
|
||||
|
||||
ScanlineMask::~ScanlineMask()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
31
PortabilityLayer/ScanlineMask.h
Normal file
31
PortabilityLayer/ScanlineMask.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
class ScanlineMaskBuilder;
|
||||
|
||||
class ScanlineMask
|
||||
{
|
||||
public:
|
||||
void Destroy();
|
||||
|
||||
static ScanlineMask *Create(const ScanlineMaskBuilder &builder);
|
||||
|
||||
private:
|
||||
enum DataStorage
|
||||
{
|
||||
DataStorage_UInt8,
|
||||
DataStorage_UInt16,
|
||||
DataStorage_UInt32,
|
||||
};
|
||||
|
||||
explicit ScanlineMask(DataStorage dataStorage, const void *data, size_t numSpans);
|
||||
~ScanlineMask();
|
||||
|
||||
const DataStorage m_dataStorage;
|
||||
const void *m_data;
|
||||
const size_t m_numSpans;
|
||||
};
|
||||
}
|
||||
66
PortabilityLayer/ScanlineMaskBuilder.cpp
Normal file
66
PortabilityLayer/ScanlineMaskBuilder.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "ScanlineMaskBuilder.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
ScanlineMaskBuilder::ScanlineMaskBuilder()
|
||||
: m_spans(nullptr)
|
||||
, m_numSpans(0)
|
||||
, m_capacity(0)
|
||||
, m_longestSpan(0)
|
||||
{
|
||||
}
|
||||
|
||||
ScanlineMaskBuilder::~ScanlineMaskBuilder()
|
||||
{
|
||||
if (m_spans)
|
||||
realloc(m_spans, 0);
|
||||
}
|
||||
|
||||
bool ScanlineMaskBuilder::AppendSpan(size_t span)
|
||||
{
|
||||
if (span > m_longestSpan)
|
||||
m_longestSpan = span;
|
||||
|
||||
if (m_capacity == 0)
|
||||
{
|
||||
m_capacity = 8;
|
||||
m_spans = static_cast<size_t*>(realloc(nullptr, sizeof(size_t) * m_capacity));
|
||||
if (!m_spans)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_numSpans == m_capacity)
|
||||
{
|
||||
if (m_capacity >= (SIZE_MAX / sizeof(size_t) / 2))
|
||||
return false;
|
||||
|
||||
m_capacity *= 2;
|
||||
void *newSpans = realloc(m_spans, sizeof(size_t) * m_capacity);
|
||||
if (!newSpans)
|
||||
return false;
|
||||
|
||||
m_spans = static_cast<size_t*>(newSpans);
|
||||
}
|
||||
|
||||
m_spans[m_numSpans++] = span;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t ScanlineMaskBuilder::GetLongestSpan() const
|
||||
{
|
||||
return m_longestSpan;
|
||||
}
|
||||
|
||||
const size_t *ScanlineMaskBuilder::GetSpans() const
|
||||
{
|
||||
return m_spans;
|
||||
}
|
||||
|
||||
size_t ScanlineMaskBuilder::GetNumSpans() const
|
||||
{
|
||||
return m_numSpans;
|
||||
}
|
||||
}
|
||||
25
PortabilityLayer/ScanlineMaskBuilder.h
Normal file
25
PortabilityLayer/ScanlineMaskBuilder.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
class ScanlineMaskBuilder
|
||||
{
|
||||
public:
|
||||
ScanlineMaskBuilder();
|
||||
~ScanlineMaskBuilder();
|
||||
|
||||
bool AppendSpan(size_t span);
|
||||
|
||||
size_t GetLongestSpan() const;
|
||||
const size_t *GetSpans() const;
|
||||
size_t GetNumSpans() const;
|
||||
|
||||
private:
|
||||
size_t *m_spans;
|
||||
size_t m_numSpans;
|
||||
size_t m_capacity;
|
||||
size_t m_longestSpan;
|
||||
};
|
||||
}
|
||||
318
PortabilityLayer/ScanlineMaskConverter.cpp
Normal file
318
PortabilityLayer/ScanlineMaskConverter.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "ScanlineMaskConverter.h"
|
||||
#include "ScanlineMask.h"
|
||||
#include "Vec2i.h"
|
||||
#include "LinePlotter.h"
|
||||
#include "ScanlineMaskBuilder.h"
|
||||
#include "IPlotter.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
#define PL_SCANLINE_MASKS_DEBUGGING 1
|
||||
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
#include "stb_image_write.h"
|
||||
#endif
|
||||
|
||||
// The algorithm that we're trying to emulate consistently paints on covered pixels of a line as in-bounds,
|
||||
// while also identifying which pixels are part of the poly interior.
|
||||
//
|
||||
// We use a plotting algorithm to generate an outline with some rules:
|
||||
// - If a plot moves vertically in the same direction as the previous vertical movement, then the inversion bit for the pixel is flipped.
|
||||
// - A scanline is then run across the row, toggling state between inversion points, and filling any that contain a plot point.
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
enum PlotterVertical
|
||||
{
|
||||
PlotterVertical_NegY,
|
||||
PlotterVertical_PosY,
|
||||
|
||||
PlotterVertical_None,
|
||||
};
|
||||
|
||||
static void FillPresenceFlag(uint8_t *bitfield, size_t element)
|
||||
{
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
bitfield[element * 4] = 255;
|
||||
#else
|
||||
bitfield[element / 4] |= 1 << ((element & 3) * 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ReadPresenceFlag(const uint8_t *bitfield, size_t element)
|
||||
{
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
return bitfield[element * 4] == 255;;
|
||||
#else
|
||||
return (bitfield[element / 4] & (1 << ((element & 3) * 2))) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void FlipBorderFlag(uint8_t *bitfield, size_t element)
|
||||
{
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
bitfield[element * 4 + 1] ^= 255;
|
||||
#else
|
||||
bitfield[element / 4] ^= 2 << ((element & 3) * 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool ReadBorderFlag(const uint8_t *bitfield, size_t element)
|
||||
{
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
return bitfield[element * 4 + 1] == 255;
|
||||
#else
|
||||
return (bitfield[element / 4] & (2 << ((element & 3) * 2))) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static PlotterVertical VerticalForPlotDir(PlotDirection plotDir)
|
||||
{
|
||||
switch (plotDir)
|
||||
{
|
||||
case PlotDirection_NegX_NegY:
|
||||
case PlotDirection_0X_NegY:
|
||||
case PlotDirection_PosX_NegY:
|
||||
return PlotterVertical_NegY;
|
||||
case PlotDirection_NegX_PosY:
|
||||
case PlotDirection_0X_PosY:
|
||||
case PlotDirection_PosX_PosY:
|
||||
return PlotterVertical_PosY;
|
||||
default:
|
||||
return PlotterVertical_None;
|
||||
}
|
||||
}
|
||||
|
||||
static bool FlushScanline(const uint8_t *flagBits, size_t firstElement, size_t width, ScanlineMaskBuilder &maskBuilder)
|
||||
{
|
||||
size_t spanStart = 0;
|
||||
bool isBorderToggleActive = false;
|
||||
bool maskSpanState = false;
|
||||
|
||||
for (size_t col = 0; col < width; col++)
|
||||
{
|
||||
const size_t element = firstElement + col;
|
||||
|
||||
const bool presenceFlag = ReadPresenceFlag(flagBits, element);
|
||||
const bool borderFlag = ReadBorderFlag(flagBits, element);
|
||||
|
||||
if (borderFlag)
|
||||
isBorderToggleActive = !isBorderToggleActive;
|
||||
|
||||
const bool elementState = (isBorderToggleActive || presenceFlag);
|
||||
|
||||
if (elementState != maskSpanState)
|
||||
{
|
||||
if (!maskBuilder.AppendSpan(col - spanStart))
|
||||
return false;
|
||||
|
||||
spanStart = col;
|
||||
maskSpanState = elementState;
|
||||
}
|
||||
}
|
||||
|
||||
if (!maskBuilder.AppendSpan(width - spanStart))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ScanlineMask *ComputePlot(uint32_t width, uint32_t height, const Vec2i &minPoint, IPlotter &plotter)
|
||||
{
|
||||
assert(width > 0 && height > 0);
|
||||
|
||||
if (static_cast<uint64_t>(width) * static_cast<uint64_t>(height) > SIZE_MAX)
|
||||
return nullptr;
|
||||
|
||||
const size_t numElements = width * height;
|
||||
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
const size_t storageSize = numElements * 4;
|
||||
#else
|
||||
const size_t storageSize = (numElements * 2 + 7) / 8;
|
||||
#endif
|
||||
void *storage = malloc(storageSize);
|
||||
|
||||
if (!storage)
|
||||
return nullptr;
|
||||
|
||||
uint8_t *flagBits = static_cast<uint8_t*>(storage);
|
||||
memset(flagBits, 0, storageSize);
|
||||
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
{
|
||||
for (size_t i = 0; i < storageSize; i += 4)
|
||||
flagBits[i + 3] = 255;
|
||||
}
|
||||
#endif
|
||||
|
||||
const Vec2i initialPoint = plotter.GetPoint() - minPoint;
|
||||
|
||||
bool haveFirstVertical = false;
|
||||
size_t firstVerticalElement = 0; // First element that contains a vertical movement on the first row
|
||||
PlotterVertical firstVertical = PlotterVertical::PlotterVertical_None;
|
||||
PlotterVertical lastVertical = PlotterVertical::PlotterVertical_None;
|
||||
|
||||
size_t currentElement = static_cast<uint32_t>(initialPoint.m_y) * width + static_cast<uint32_t>(initialPoint.m_x);
|
||||
FillPresenceFlag(flagBits, currentElement);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const size_t prevElement = currentElement;
|
||||
|
||||
const PlotDirection plotDir = plotter.PlotNext();
|
||||
if (plotDir == PlotDirection_Exhausted)
|
||||
break;
|
||||
|
||||
switch (plotDir)
|
||||
{
|
||||
case PlotDirection_NegX_NegY:
|
||||
currentElement = currentElement - 1 - width;
|
||||
break;
|
||||
case PlotDirection_0X_NegY:
|
||||
currentElement = currentElement - width;
|
||||
break;
|
||||
case PlotDirection_PosX_NegY:
|
||||
currentElement = currentElement + 1 - width;
|
||||
break;
|
||||
|
||||
case PlotDirection_NegX_PosY:
|
||||
currentElement = currentElement + width - 1;
|
||||
break;
|
||||
case PlotDirection_0X_PosY:
|
||||
currentElement = currentElement + width;
|
||||
break;
|
||||
case PlotDirection_PosX_PosY:
|
||||
currentElement = currentElement + width + 1;
|
||||
break;
|
||||
|
||||
case PlotDirection_NegX_0Y:
|
||||
currentElement = currentElement - 1;
|
||||
break;
|
||||
case PlotDirection_PosX_0Y:
|
||||
currentElement = currentElement + 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
FillPresenceFlag(flagBits, currentElement);
|
||||
|
||||
#if PL_SCANLINE_MASKS_DEBUGGING
|
||||
const Vec2i expectedPoint = plotter.GetPoint() - minPoint;
|
||||
const int64_t expectedElement = static_cast<uint32_t>(expectedPoint.m_y) * width + expectedPoint.m_x;
|
||||
assert(currentElement == expectedElement);
|
||||
#endif
|
||||
assert(currentElement < numElements);
|
||||
|
||||
const PlotterVertical verticalForPlotDir = VerticalForPlotDir(plotDir);
|
||||
if (verticalForPlotDir != PlotterVertical_None)
|
||||
{
|
||||
if (!haveFirstVertical)
|
||||
{
|
||||
haveFirstVertical = true;
|
||||
firstVerticalElement = prevElement;
|
||||
firstVertical = verticalForPlotDir;
|
||||
}
|
||||
|
||||
if (verticalForPlotDir == lastVertical)
|
||||
FlipBorderFlag(flagBits, prevElement);
|
||||
|
||||
lastVertical = verticalForPlotDir;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastVertical == firstVertical)
|
||||
FlipBorderFlag(flagBits, firstVerticalElement);
|
||||
|
||||
assert(plotter.GetPoint() - minPoint == initialPoint);
|
||||
|
||||
ScanlineMaskBuilder maskBuilder;
|
||||
|
||||
bool failed = false;
|
||||
for (size_t rowFirstElement = 0; rowFirstElement < numElements; rowFirstElement += width)
|
||||
{
|
||||
if (!FlushScanline(flagBits, rowFirstElement, width, maskBuilder))
|
||||
{
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(storage);
|
||||
|
||||
return ScanlineMask::Create(maskBuilder);
|
||||
}
|
||||
|
||||
class PolyPlotter final : public IPlotter
|
||||
{
|
||||
public:
|
||||
PolyPlotter(const Vec2i *points, size_t numPoints);
|
||||
|
||||
PlotDirection PlotNext() override;
|
||||
const Vec2i &GetPoint() const override;
|
||||
|
||||
private:
|
||||
LinePlotter m_linePlotter;
|
||||
const Vec2i *m_points;
|
||||
size_t m_currentTargetPoint;
|
||||
size_t m_numPoints;
|
||||
};
|
||||
|
||||
PolyPlotter::PolyPlotter(const Vec2i *points, size_t numPoints)
|
||||
: m_points(points)
|
||||
, m_currentTargetPoint(0)
|
||||
, m_numPoints(numPoints)
|
||||
{
|
||||
m_linePlotter.Reset(points[numPoints - 1], points[0]);
|
||||
}
|
||||
|
||||
PlotDirection PolyPlotter::PlotNext()
|
||||
{
|
||||
if (m_currentTargetPoint == m_numPoints)
|
||||
return PlotDirection_Exhausted;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const PlotDirection plotDir = m_linePlotter.PlotNext();
|
||||
if (plotDir == PlotDirection_Exhausted)
|
||||
{
|
||||
m_currentTargetPoint++;
|
||||
if (m_currentTargetPoint == m_numPoints)
|
||||
return PlotDirection_Exhausted;
|
||||
|
||||
m_linePlotter.Reset(m_points[m_currentTargetPoint - 1], m_points[m_currentTargetPoint]);
|
||||
}
|
||||
else
|
||||
return plotDir;
|
||||
}
|
||||
}
|
||||
|
||||
const Vec2i &PolyPlotter::GetPoint() const
|
||||
{
|
||||
return m_linePlotter.GetPoint();
|
||||
}
|
||||
|
||||
ScanlineMask *ScanlineMaskConverter::CompilePoly(const Vec2i *points, size_t numPoints)
|
||||
{
|
||||
assert(numPoints > 0);
|
||||
|
||||
Vec2i minPoint = points[0];
|
||||
Vec2i maxPoint = points[0];
|
||||
|
||||
for (size_t i = 1; i < numPoints; i++)
|
||||
{
|
||||
minPoint.m_x = std::min<int32_t>(minPoint.m_x, points[i].m_x);
|
||||
minPoint.m_y = std::min<int32_t>(minPoint.m_y, points[i].m_y);
|
||||
maxPoint.m_x = std::max<int32_t>(maxPoint.m_x, points[i].m_x);
|
||||
maxPoint.m_y = std::max<int32_t>(maxPoint.m_y, points[i].m_y);
|
||||
}
|
||||
|
||||
const uint32_t width = static_cast<uint32_t>(maxPoint.m_x - minPoint.m_x) + 1;
|
||||
const uint32_t height = static_cast<uint32_t>(maxPoint.m_y - minPoint.m_y) + 1;
|
||||
|
||||
PolyPlotter polyPlotter(points, numPoints);
|
||||
return ComputePlot(width, height, minPoint, polyPlotter);
|
||||
}
|
||||
}
|
||||
15
PortabilityLayer/ScanlineMaskConverter.h
Normal file
15
PortabilityLayer/ScanlineMaskConverter.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace PortabilityLayer
|
||||
{
|
||||
class ScanlineMask;
|
||||
struct Vec2i;
|
||||
|
||||
class ScanlineMaskConverter
|
||||
{
|
||||
public:
|
||||
static ScanlineMask *CompilePoly(const Vec2i *points, size_t numPoints);
|
||||
};
|
||||
}
|
||||
@@ -17,7 +17,10 @@ namespace PortabilityLayer
|
||||
Vec2i operator-(const Vec2i &other) const;
|
||||
|
||||
Vec2i &operator+=(const Vec2i &other);
|
||||
Vec2i &operator-=(const Vec2i &other);
|
||||
Vec2i &operator-=(const Vec2i &other);
|
||||
|
||||
bool operator==(const Vec2i &other) const;
|
||||
bool operator!=(const Vec2i &other) const;
|
||||
};
|
||||
|
||||
inline Vec2i::Vec2i()
|
||||
@@ -60,6 +63,15 @@ namespace PortabilityLayer
|
||||
m_x -= other.m_x;
|
||||
m_y -= other.m_y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool Vec2i::operator==(const Vec2i &other) const
|
||||
{
|
||||
return m_x == other.m_x && m_y == other.m_y;
|
||||
}
|
||||
|
||||
inline bool Vec2i::operator!=(const Vec2i &other) const
|
||||
{
|
||||
return !((*this) == other);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user