Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image loader #18

Merged
merged 4 commits into from
Apr 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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