Skip to content

Commit

Permalink
Merge pull request #18 from 0xABADCAFE/issues/issue-6/more-routines
Browse files Browse the repository at this point in the history
Image loader - absolute mvp
  • Loading branch information
0xABADCAFE authored Apr 18, 2021
2 parents 7c03f5c + 5b5b9a5 commit 90a189f
Show file tree
Hide file tree
Showing 11 changed files with 333 additions and 16 deletions.
30 changes: 30 additions & 0 deletions demos/image.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"displays": {
"default": {
"sType": "DoubleVerticalRGB",
"iWidth": 100,
"iHeight": 100,
"iMaxFPS": 30
}
},
"routines": {
"image": {
"sType": "RGBImage",
"iPriority": 0,
"aParameters": {
"sPath": "images/slipped_disc_1.ppm"
}
}
},
"events": [
{
"at": 0.0,
"on": "routine/image",
"do": "enable"
},
{
"at": 0.1,
"do": "end"
}
]
}
Binary file added demos/images/slipped_disc_1.ppm
Binary file not shown.
3 changes: 3 additions & 0 deletions src/classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
'ABadCafe\\PDE\\Routine\\SimpleLine' => '/routine/SimpleLine.php',
'ABadCafe\\PDE\\Routine\\RGBPulse' => '/routine/RGBPulse.php',
'ABadCafe\\PDE\\Routine\\Toroid' => '/routine/Toroid.php',
'ABadCafe\\PDE\\Routine\\RGBImage' => '/routine/RGBImage.php',
'ABadCafe\\PDE\\Routine\\Factory' => '/routine/Factory.php',
'ABadCafe\\PDE\\Routine\\RGBPersistence' => '/routine/RGBPersistence.php',
'ABadCafe\\PDE\\Routine\\StaticNoise' => '/routine/StaticNoise.php',
'ABadCafe\\PDE\\Routine\\IResourceLoader' => '/routine/common/IResourceLoader.php',
'ABadCafe\\PDE\\Routine\\TResourceLoader' => '/routine/common/TResourceLoader.php',
'ABadCafe\\PDE\\Routine\\Base' => '/routine/common/Base.php',
'ABadCafe\\PDE\\System\\ILoader' => '/system/ILoader.php',
'ABadCafe\\PDE\\System\\IRateLimiter' => '/system/IRateLimiter.php',
Expand Down
86 changes: 73 additions & 13 deletions src/display/DoubleVerticalRGB.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,33 @@ private function subprocessRenderLoop() {
$iExpectSize = $this->iWidth * $this->iHeight * 4;
$sTemplate = IANSIControl::ATTR_BG_RGB_TPL;
$iShortReads = 0;

$aTemplates = [
// Everything changed
0 => IANSIControl::ATTR_FG_RGB_TPL . IANSIControl::ATTR_BG_RGB_TPL . ICustomChars::MAP[0x80],

// Foreground and Background are equal but changed
1 => IANSIControl::ATTR_BG_RGB_TPL . ' ',

// Foreground and Background unequal, foreground unchanged
2 => IANSIControl::ATTR_BG_RGB_TPL . ICustomChars::MAP[0x80],

// Foreground and Background equal, foreground unchanged
3 => IANSIControl::ATTR_BG_RGB_TPL . ' ',

// Foreground and Background unequal, background unchanged
4 => IANSIControl::ATTR_FG_RGB_TPL . ICustomChars::MAP[0x80],

// Foreground and Backgrounc equal, foreground unchanged
5 => IANSIControl::ATTR_BG_RGB_TPL . ' ',

// Foreground and Background unequal, unchanged
6 => ICustomChars::MAP[0x80],

// Foreground and background unequal, unchanged
7 => ' '
];

while (($sInput = $this->receivePixelData($iExpectSize))) {

$iGotSize = strlen($sInput);
Expand All @@ -108,23 +135,56 @@ private function subprocessRenderLoop() {
$iEvenOffset = 0;
$iOddOffset = $this->iWidth;

// Todo optimise for cases where either value is unchanged
$sTemplate = IANSIControl::ATTR_FG_RGB_TPL . IANSIControl::ATTR_BG_RGB_TPL . ICustomChars::MAP[0x80];
$iLastBackRGB = 0;
$iLastForeRGB = 0;

for ($iRow = 0; $iRow < $this->iHeight; $iRow += 2) {
$i = $this->iWidth;
while ($i--) {
$iForeRGB = $aPixels[$iEvenOffset++];
$iBackRGB = $aPixels[$iOddOffset++];
$sRawBuffer .= sprintf(
$sTemplate,
$iForeRGB >> 16,
($iForeRGB >> 8) & 0xFF,
($iForeRGB & 0xFF),
$iBackRGB >> 16,
($iBackRGB >> 8) & 0xFF,
($iBackRGB & 0xFF)
);
$iForeRGB = $aPixels[$iEvenOffset++];
$iBackRGB = $aPixels[$iOddOffset++];
$iCase = (int)($iForeRGB == $iBackRGB) | (int)($iForeRGB == $iLastForeRGB) << 1 | (int)($iBackRGB == $iLastBackRGB) << 2;
$sTemplate = $aTemplates[$iCase];
switch ($iCase) {
case 1:
//case 2: //TODO - why does this glitch?
case 3:
case 5:
$sRawBuffer .= sprintf(
$sTemplate,
$iBackRGB >> 16,
($iBackRGB >> 8) & 0xFF,
($iBackRGB & 0xFF)
);
break;
case 4:
$sRawBuffer .= sprintf(
$sTemplate,
$iForeRGB >> 16,
($iForeRGB >> 8) & 0xFF,
($iForeRGB & 0xFF)
);
break;
case 6:
case 7:
$sRawBuffer .= $sTemplate;
break;
case 0:
default:
$sRawBuffer .= sprintf(
$aTemplates[0],
$iForeRGB >> 16,
($iForeRGB >> 8) & 0xFF,
($iForeRGB & 0xFF),
$iBackRGB >> 16,
($iBackRGB >> 8) & 0xFF,
($iBackRGB & 0xFF)
);

}
$iLastForeRGB = $iForeRGB;
$iLastBackRGB = $iBackRGB;

}
$iEvenOffset += $this->iWidth;
$iOddOffset += $this->iWidth;
Expand Down
1 change: 1 addition & 0 deletions src/routine/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Factory {
'Toroid' => Toroid::class,
'RGBPulse' => RGBPulse::class,
'RGBPersistence' => RGBPersistence::class,
'RGBImage' => RGBImage::class,
];

private static ?self $oInstance = null;
Expand Down
103 changes: 103 additions & 0 deletions src/routine/RGBImage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?php
/**
* ______ __
* __ /\\\\\\\\_ /\\\
* /\\\ /\\\//////\\\_ \/\\\
* /\\\// \/// \//\\\ ________ ___\/\\\ _______
* /\\\// /\\\ /\\\\\\\\\_ /\\\\\\\\\ /\\\\\\\\_
* /\\\//_ /\\\\/ /\\\/////\\\ /\\\////\\\ /\\\/////\\\
* \////\\\ __ /\\\/ \/\\\ \/\\\ \/\\\ \/\\\ /\\\\\\\\\\\
* \////\\\ __ \///_ \/\\\___\/\\\ \/\\\__\/\\\ \//\\\//////_
* \////\\\ /\\\ \/\\\\\\\\\\ \//\\\\\\\\\ \//\\\\\\\\\
* \/// \/// \/\\\////// \///////// \/////////
* \/\\\
* \///
*
* /P(?:ointless|ortable|HP) Demo Engine/
*/

declare(strict_types=1);

namespace ABadCafe\PDE\Routine;

use ABadCafe\PDE;
use \SPLFixedArray;

/**
* Display an image
*
* TODO controls and optimise
*/
class RGBImage extends Base implements IResourceLoader {

use TResourceLoader;

private int $iWidth, $iHeight, $iViewWidth, $iViewHeight;

private SPLFixedArray $oPixels;

const DEFAULT_PARAMETERS = [
'sPath' => 'required'
];

public function preload() : self {
$this->loadPNM($this->oParameters->sPath);
return $this;
}

/**
* @inheritDoc
*/
public function setDisplay(PDE\IDisplay $oDisplay) : self {
$this->bCanRender = ($oDisplay instanceof PDE\Display\IPixelled);
$this->oDisplay = $oDisplay;
$this->iViewWidth = $oDisplay->getWidth();
$this->iViewHeight = $oDisplay->getHeight();
return $this;
}

/**
* @inheritDoc
*/
public function render(int $iFrameNumber, float $fTimeIndex) : self {
if ($this->canRender($iFrameNumber, $fTimeIndex)) {
$oBuffer = $this->oDisplay->getPixelBuffer();
if ($this->iWidth == $this->iViewWidth && $this->iHeight == $this->iViewHeight) {
foreach ($oBuffer as $i => $iBufferRGB) {
$oBuffer[$i] = $this->oPixels[$i];
}
}
}
return $this;
}

/**
* @inheritDoc
*/
protected function parameterChange() {
}

/**
* Load a PNM image
*/
protected function loadPNM(string $sPath) {
$sRaw = $this->loadFile($sPath);
if (preg_match('/^(\d+)\s+(\d+)$/m', $sRaw, $aMatches)) {
$this->iWidth = (int)$aMatches[1];
$this->iHeight = (int)$aMatches[2];
$iArea = $this->iWidth * $this->iHeight;
$this->oPixels = new SPLFixedArray($iArea);
$sData = substr($sRaw, ($iArea * -3));
$iDataOffset = 0;
for ($i = 0; $i < $iArea; ++$i) {
$this->oPixels[$i] =
(ord($sData[$iDataOffset++]) << 16) |
(ord($sData[$iDataOffset++]) << 8) |
(ord($sData[$iDataOffset++]));
}
} else {
throw new \Exception('Invalid PNM Format');
}
}

}
46 changes: 46 additions & 0 deletions src/routine/common/IResourceLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/**
* ______ __
* __ /\\\\\\\\_ /\\\
* /\\\ /\\\//////\\\_ \/\\\
* /\\\// \/// \//\\\ ________ ___\/\\\ _______
* /\\\// /\\\ /\\\\\\\\\_ /\\\\\\\\\ /\\\\\\\\_
* /\\\//_ /\\\\/ /\\\/////\\\ /\\\////\\\ /\\\/////\\\
* \////\\\ __ /\\\/ \/\\\ \/\\\ \/\\\ \/\\\ /\\\\\\\\\\\
* \////\\\ __ \///_ \/\\\___\/\\\ \/\\\__\/\\\ \//\\\//////_
* \////\\\ /\\\ \/\\\\\\\\\\ \//\\\\\\\\\ \//\\\\\\\\\
* \/// \/// \/\\\////// \///////// \/////////
* \/\\\
* \///
*
* /P(?:ointless|ortable|HP) Demo Engine/
*/

declare(strict_types=1);

namespace ABadCafe\PDE\Routine;

use ABadCafe\PDE;

/**
* IResourceLoader
*
* Tag interface for routines that need to be able to load additional data
*/
interface IResourceLoader {

/**
* Set the base path
*
* @param string $sBasePath
* @return self
*/
public function setBasePath(string $sBasePath) : self;

/**
* Preload any resources
*
* @return self
*/
public function preload() : self;
}
51 changes: 51 additions & 0 deletions src/routine/common/TResourceLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* ______ __
* __ /\\\\\\\\_ /\\\
* /\\\ /\\\//////\\\_ \/\\\
* /\\\// \/// \//\\\ ________ ___\/\\\ _______
* /\\\// /\\\ /\\\\\\\\\_ /\\\\\\\\\ /\\\\\\\\_
* /\\\//_ /\\\\/ /\\\/////\\\ /\\\////\\\ /\\\/////\\\
* \////\\\ __ /\\\/ \/\\\ \/\\\ \/\\\ \/\\\ /\\\\\\\\\\\
* \////\\\ __ \///_ \/\\\___\/\\\ \/\\\__\/\\\ \//\\\//////_
* \////\\\ /\\\ \/\\\\\\\\\\ \//\\\\\\\\\ \//\\\\\\\\\
* \/// \/// \/\\\////// \///////// \/////////
* \/\\\
* \///
*
* /P(?:ointless|ortable|HP) Demo Engine/
*/

declare(strict_types=1);

namespace ABadCafe\PDE\Routine;

use ABadCafe\PDE;

/**
* TResourceLoader
*
* Common implementation for IResourceLoader
*/
trait TResourceLoader {

public function setBasePath(string $sBasePath) : self {
$this->sBasePath = $sBasePath;
return $this;
}

/**
* Load a file.
*
* @param string $sRelativePath
* @return string
* @throws \Exception
*/
private function loadFile(string $sRelativePath) : string {
$sPath = $this->sBasePath . $sRelativePath;
if (file_exists($sPath) && is_readable($sPath)) {
return file_get_contents($sPath);
}
throw new \Exception($sPath . ' could not be read');
}
}
11 changes: 8 additions & 3 deletions src/system/Context.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class Context {
*/
public function __construct(ILoader $oLoader) {
$this->initialiseDisplays($oLoader->getDisplays());
$this->initialiseRoutines($oLoader->getRoutines());
$this->initialiseRoutines($oLoader->getRoutines(), $oLoader->getBasePath());
$this->initialiseTimeline($oLoader->getEvents());
}

Expand Down Expand Up @@ -120,18 +120,23 @@ private function initialiseDisplays(array $aDisplayDefinitions) {
*
* @param Definition\Routine[] $aRoutineDefinitions
*/
private function initialiseRoutines(array $aRoutineDefinitions) {
private function initialiseRoutines(array $aRoutineDefinitions, string $sBasePath) {
$oRoutineFactory = PDE\Routine\Factory::get();
foreach ($aRoutineDefinitions as $sIdentity => $oRoutineDefinition) {
$sIdentity = self::NS_ROUTINE . $sIdentity;
if (isset($this->aRoutineInstances[$sIdentity])) {
throw new \Exception('Duplicate routine identity ' . $sIdentity);
}
$this->aRoutineInstances[$sIdentity] = $oRoutineFactory->create(
$this->aRoutineInstances[$sIdentity] = $oRoutine = $oRoutineFactory->create(
$oRoutineDefinition->sType,
$this->oDisplay,
$oRoutineDefinition->aParameters
);
if ($oRoutine instanceof PDE\Routine\IResourceLoader) {
$oRoutine
->setBasePath($sBasePath)
->preload();
}
$this->aRoutinePriorities[$sIdentity] = $oRoutineDefinition->iPriority;
}
asort($this->aRoutinePriorities, SORT_NUMERIC);
Expand Down
Loading

0 comments on commit 90a189f

Please sign in to comment.