Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: libvips/php-vips
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: libvips/php-vips
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: callback-experiment
Choose a head ref
Can’t automatically merge. Don’t worry, you can still create the pull request.
  • 7 commits
  • 14 files changed
  • 1 contributor

Commits on Mar 5, 2023

  1. Copy the full SHA
    8ac05a4 View commit details
  2. Copy the full SHA
    4ac58f3 View commit details

Commits on Apr 7, 2023

  1. Copy the full SHA
    aa39f88 View commit details

Commits on Apr 8, 2023

  1. Copy the full SHA
    1796d42 View commit details
  2. comment out some dbg code

    jcupitt committed Apr 8, 2023
    Copy the full SHA
    30174d0 View commit details
  3. use a closure to hold the callback

    sigh
    jcupitt committed Apr 8, 2023
    Copy the full SHA
    c3109d4 View commit details

Commits on Apr 11, 2023

  1. more fiddling

    jcupitt committed Apr 11, 2023
    Copy the full SHA
    01da7e6 View commit details
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ All notable changes to `:vips` will be documented in this file.
## master

- improve FFI startup [West14]
- revise example use of composer [jcupitt]

## 2.1.1 - 2022-11-13

2 changes: 1 addition & 1 deletion examples/addconst.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/autoload.php';

use Jcupitt\Vips;

7 changes: 6 additions & 1 deletion examples/bench.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/autoload.php';

use Jcupitt\Vips;

if (count($argv) != 3) {
echo("usage: ./bench.php input-image output-image\n");
exit(1);
}

$im = Vips\Image::newFromFile($argv[1], ['access' => Vips\Access::SEQUENTIAL]);

$im = $im->crop(100, 100, $im->width - 200, $im->height - 200);
2 changes: 1 addition & 1 deletion examples/class.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/autoload.php';

use Jcupitt\Vips;

5 changes: 0 additions & 5 deletions examples/composer.json

This file was deleted.

7 changes: 6 additions & 1 deletion examples/sig.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/autoload.php';

use Jcupitt\Vips;

@@ -141,6 +141,11 @@ function sigLAB(Vips\Image $image, bool $sharpen, float $midpoint, float $contra
]);
}

if (count($argv) != 3) {
echo("usage: ./sig.php input-image output-image\n");
exit(1);
}

$im = Vips\Image::newFromFile($argv[1], ['access' => Vips\Access::SEQUENTIAL]);

/**
Empty file modified examples/streaming-bench.php
100644 → 100755
Empty file.
Empty file modified examples/streaming.php
100644 → 100755
Empty file.
7 changes: 6 additions & 1 deletion examples/vips-magick.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/autoload.php';

use Jcupitt\Vips;

if (count($argv) != 3) {
echo("usage: ./vips-magick.php input-image output-image\n");
exit(1);
}

/* Load an image with libvips, render to a large memory buffer, wrap a imagick
* image around that, then use imagick to save as another file.
*/
4 changes: 2 additions & 2 deletions examples/watermark-image.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/autoload.php';
use Jcupitt\Vips;

#Vips\Config::setLogger(new Vips\DebugLogger());
@@ -41,4 +41,4 @@
$image = null;
$watermark = null;

Vips\Config::shutDown();
Vips\FFI::shutDown();
2 changes: 1 addition & 1 deletion examples/watermark-text.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php

require __DIR__ . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/autoload.php';
use Jcupitt\Vips;

#Vips\Config::setLogger(new Vips\DebugLogger());
15 changes: 12 additions & 3 deletions src/FFI.php
Original file line number Diff line number Diff line change
@@ -422,12 +422,16 @@ private static function init(): void
void g_object_get_property (GObject* object,
const char* name, GValue* value);
typedef void (*GCallback)(void);
typedef void (*GClosureNotify)(void* data, struct _GClosure *);
typedef void (*GCallback)(void);
typedef void (*GCallback_progress)(void*,void*,void*);
typedef gint64 (*GCallback_read)(void*,void*,gint64,void*);
long g_signal_connect_data (GObject* object,
const char* detailed_signal,
GCallback c_handler,
void* data,
gpointer data,
GClosureNotify destroy_data,
int connect_flags);
@@ -490,7 +494,6 @@ private static function init(): void
# the whole libvips API, mostly adapted from pyvips
$vips_decls = $typedefs . <<<EOS
typedef struct _VipsImage VipsImage;
typedef struct _VipsProgress VipsProgress;
// Defined in GObject, just typedef to void
typedef void GParamSpec;
@@ -803,11 +806,16 @@ private static function init(): void
self::$ctypes = [
"GObject" => self::$gobject->type("GObject*"),
"GClosure" => self::$gobject->type("GClosure"),
"GCallback" => self::$gobject->type("GCallback"),
"GCallback_read" => self::$gobject->type("GCallback_read"),
"GCallback_progress" => self::$gobject->type("GCallback_progress"),
"GType" => self::$gobject->type("GType"),
"GParamSpec" => self::$gobject->type("GParamSpec*"),
"VipsObject" => self::$vips->type("VipsObject*"),
"VipsOperation" => self::$vips->type("VipsOperation*"),
"VipsImage" => self::$vips->type("VipsImage*"),
"VipsInterpolate" => self::$vips->type("VipsInterpolate*"),
"VipsProgress" => self::$vips->type("VipsProgress*"),
"VipsConnection" => self::$vips->type("VipsConnection*"),
"VipsSource" => self::$vips->type("VipsSource*"),
"VipsSourceCustom" => self::$vips->type("VipsSourceCustom*"),
@@ -816,6 +824,7 @@ private static function init(): void
];

self::$gtypes = [
"gpointer" => self::$gobject->g_type_from_name("gpointer"),
"gboolean" => self::$gobject->g_type_from_name("gboolean"),
"gint" => self::$gobject->g_type_from_name("gint"),
"gint64" => self::$gobject->g_type_from_name("gint64"),
166 changes: 132 additions & 34 deletions src/GObject.php
Original file line number Diff line number Diff line change
@@ -53,13 +53,28 @@
*/
abstract class GObject
{
/**
*/
private static array $handleTable = [];
private static int $nextIndex = 0;

/**
* A pointer to the underlying GObject.
*
* @internal
*/
private CData $pointer;

/**
* Upstream objects we must keep alive.
*
* A set of references to other php objects which this object refers to
* via ffi, ie. references which the php GC cannot find automatically.
*
* @internal
*/
private array $_references = [];

/**
* Wrap a GObject around an underlying vips resource. The GObject takes
* ownership of the pointer and will unref it on finalize.
@@ -99,19 +114,107 @@ public function unref(): void

/**
* Connect to a signal on this object.
* The callback will be triggered every time this signal is issued on this instance.
*
* The callback will be triggered every time this signal is issued on this
* instance.
*
* @throws Exception
*/
public function signalConnect(string $name, Closure $callback): void
{
$marshaler = self::getMarshaler($name, $callback);
if ($marshaler === null) {
throw new Exception("unsupported signal $name");
echo "signalConnect:\n";

$marshal = self::getMarshal($name);
$handle = self::getHandle($callback);

$sig = \FFI::arrayType(FFI::ctypes("GCallback"), [1]);
$c_callback = \FFI::new($sig);
$c_callback[0] = $marshal;

$id = FFI::gobject()->g_signal_connect_data($this->pointer,
$name,
$c_callback[0],
$handle,
null,
0);
if ($id === 0) {
throw new Exception("unable to connect signal $name");
}

echo "signalConnect: done\n";
}

/* Ideally, we'd use the "user" pointer of signal_connect to hold the
* callback closure, but unfortunately php-ffi has no way (I think) to
* turn a C pointer to function back into a php function, so we have to
* build a custom closure for every signal connect with the callback
* embedded within it.
*/
private static function getMarshal(string $name) : CData
{
switch ($name) {
case 'preeval':
case 'eval':
case 'posteval':
$marshal = static function (
CData $imagePointer,
CData $progressPointer,
CData $handle) : void {
$image = new Image($imagePointer);
// Image() will unref on gc, so we must ref
FFI::gobject()->g_object_ref($imagePointer);

// FIXME ... maybe wrap VipsProgress as a php class?
$progress = \FFI::cast(FFI::ctypes("VipsProgress"),
$progressPointer);

$callback = self::fromHandle($handle);

$callback($image, $progress);
};

$sig = \FFI::arrayType(FFI::ctypes("GCallback_progress"), [1]);
$c_callback = \FFI::new($sig);
$c_callback[0] = $marshal;

return \FFI::cast(FFI::ctypes("GCallback"), $c_callback[0]);

case 'read':
if (FFI::atLeast(8, 9)) {
$marshal = function (
CData $sourcePointer,
CData $bufferPointer,
int $bufferLength,
CData $handle) : int {
echo "hello from read marshal!\n";
$callback = self::fromHandle($handle);
$result = 0;

$returnBuffer = $callback($bufferLength);
if ($returnBuffer !== null) {
$result = strlen($returnBuffer);
\FFI::memcpy($bufferPointer,
$returnBuffer,
$result
);
}

return $result;
};

$sig = \FFI::arrayType(FFI::ctypes("GCallback_read"), [1]);
$c_callback = \FFI::new($sig);
$c_callback[0] = $marshal;

echo "c_callback[0] = ";
print_r($c_callback[0]);
echo "\n";

return \FFI::cast(FFI::ctypes("GCallback"), $c_callback[0]);
}
}

$gc = FFI::gobject()->g_closure_new_simple(\FFI::sizeof(FFI::ctypes('GClosure')), null);
$gc->marshal = $marshaler;
FFI::gobject()->g_signal_connect_closure($this->pointer, $name, $gc, 0);
throw new Exception("unsupported signal $name");
}

private static function getMarshaler(string $name, Closure $callback): ?Closure
@@ -144,34 +247,7 @@ private static function getMarshaler(string $name, Closure $callback): ?Closure
);
$callback($image, $pr);
};
case 'read':
if (FFI::atLeast(8, 9)) {
return static function (
CData $gClosure,
CData $returnValue,
int $numberOfParams,
CData $params,
CData $hint,
?CData $data
) use (&$callback): void {
assert($numberOfParams === 4);
/*
* Signature: gint64(VipsSourceCustom* source, void* buffer, gint64 length, void* handle)
*/
$bufferLength = (int)FFI::gobject()->g_value_get_int64(\FFI::addr($params[2]));
$returnBuffer = $callback($bufferLength);
$returnBufferLength = 0;

if ($returnBuffer !== null) {
$returnBufferLength = strlen($returnBuffer);
$bufferPointer = FFI::gobject()->g_value_get_pointer(\FFI::addr($params[1]));
\FFI::memcpy($bufferPointer, $returnBuffer, $returnBufferLength);
}
FFI::gobject()->g_value_set_int64($returnValue, $returnBufferLength);
};
}

return null;
case 'seek':
if (FFI::atLeast(8, 9)) {
return static function (
@@ -258,6 +334,28 @@ private static function getMarshaler(string $name, Closure $callback): ?Closure
return null;
}
}

private static function getHandle($object) : CData
{
$index = self::$nextIndex;
self::$nextIndex += 1;

self::$handleTable[$index] = $object;

// hide the index inside a void*
$x = \FFI::new(FFI::ctypes("GType"));
$x->cdata = $index;

return \FFI::cast("void*", $x);
}

private static function fromHandle($handle)
{
// recover the index from a void*
$index = $handle->cdata;

return self::$handleTable[$index];
}
}

/*
14 changes: 14 additions & 0 deletions src/Image.php
Original file line number Diff line number Diff line change
@@ -1296,6 +1296,20 @@ public function remove(string $name): void
}
}

/**
* Enable progress reporting on an image.
*
* When progress reporting is enabled, evaluation of the most downstream
* image from this image will report progress using the ::preeval, ::eval,
* and ::posteval signals.
*
* @param bool $progress True to enable progress reporting
*/
public function setProgress($progress): void
{
FFI::vips()->vips_image_set_progress($this->pointer, $progress);
}

/**
* Makes a string-ified version of the Image.
*