Skip to content

Commit cece649

Browse files
committed
implement arrayaccess methods
assign and unset image bands
1 parent 456cbad commit cece649

File tree

3 files changed

+201
-13
lines changed

3 files changed

+201
-13
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22
All notable changes to `:vips` will be documented in this file.
33

4+
## 1.0.2 - 2017-06-06
5+
6+
### Added
7+
- implement array access interface [John Cupitt]
8+
49
## 1.0.1 - 2017-04-29
510

611
### Added

src/Image.php

+110-13
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@
344344
* to append a constant 255 band to an image, perhaps to add an alpha channel. Of
345345
* course you can also write:
346346
*
347-
* ```ruby
347+
* ```php
348348
* $result = $image->bandjoin($image2);
349349
* $result = $image->bandjoin([image2, image3]);
350350
* $result = Image::bandjoin([image1, image2, image3]);
@@ -353,6 +353,43 @@
353353
*
354354
* and so on.
355355
*
356+
* # Array access
357+
*
358+
* Images can be treated as arrays of bands. You can write:
359+
*
360+
* ```php
361+
* $result = $image[1];
362+
* ```
363+
*
364+
* to get band 1 from an image (green, in an RGB image).
365+
*
366+
* You can assign to bands as well. You can write:
367+
*
368+
* ```php
369+
* $image[1] = $other_image;
370+
* ```
371+
*
372+
* And band 1 will be replaced by all the bands in `$other_image` using
373+
* `bandjoin`. Use no offset to mean append, use -1 to mean prepend:
374+
*
375+
* ```php
376+
* $image[] = $other_image; // append bands from other
377+
* $image[-1] = $other_image; // prepend bands from other
378+
* ```
379+
*
380+
* You can use number and array constants as well, for example:
381+
*
382+
* ```php
383+
* $image[] = 255; // append a constant 255
384+
* $image[1] = [1, 2, 3]; // swap band 1 for three constant bands
385+
* ```
386+
*
387+
* Finally, you can delete bands with `unset`:
388+
*
389+
* ```php
390+
* unset($image[1]); // remove band 1
391+
* ```
392+
*
356393
* # Exceptions
357394
*
358395
* The wrapper spots errors from vips operations and throws
@@ -1162,7 +1199,7 @@ public function hasAlpha(): bool
11621199
}
11631200

11641201
/**
1165-
* Our ArrayAccess interface ... we allow [] to get band.
1202+
* Does band exist in image.
11661203
*
11671204
* @param mixed $offset The index to fetch.
11681205
*
@@ -1174,7 +1211,7 @@ public function offsetExists($offset): bool
11741211
}
11751212

11761213
/**
1177-
* Our ArrayAccess interface ... we allow [] to get band.
1214+
* Get band from image.
11781215
*
11791216
* @param mixed $offset The index to fetch.
11801217
*
@@ -1186,28 +1223,88 @@ public function offsetGet($offset): Image
11861223
}
11871224

11881225
/**
1189-
* Our ArrayAccess interface ... we allow [] to get band.
1226+
* Set a band.
1227+
*
1228+
* Use `$image[1] = $other_image;' to remove band 1 from this image,
1229+
* replacing it with all the bands in `$other_image`.
1230+
*
1231+
* Use `$image[] = $other_image;' to append all the bands in `$other_image`
1232+
* to `$image`.
11901233
*
1191-
* @param mixed $offset The index to set.
1234+
* Use `$image[-1] = $other_image;` to prepend all the bands in
1235+
* `$other_image` to `$image`.
1236+
*
1237+
* You can use constants or arrays in place of `$other_image`. Use `$image[]
1238+
* = 255;` to append a constant 255 band, for example, or `$image[1]
1239+
* = [1, 2];` to replace band 1 with two constant bands.
1240+
*
1241+
* @param int $offset The index to set.
11921242
* @param Image $value The band to insert
11931243
*
1194-
* @return Image the expanded image.
1244+
* @return void
11951245
*/
1196-
public function offsetSet($offset, $value): Image
1246+
public function offsetSet($offset, $value): void
11971247
{
1198-
throw new \BadMethodCallException('Image::offsetSet: not implemented');
1248+
// no offset means append
1249+
if (is_null($offset)) {
1250+
$offset = $this->bands;
1251+
}
1252+
1253+
if (!is_int($offset)) {
1254+
throw new \BadMethodCallException('Image::offsetSet: offset is not integer or null');
1255+
}
1256+
1257+
// expand constant, if necessary
1258+
if (!($value instanceof Image)) {
1259+
$value = self::imageize($this, $value);
1260+
}
1261+
1262+
// number of bands to the left and right of $value
1263+
$n_left = min($this->bands, max(0, $offset));
1264+
$n_right = min($this->bands, max(0, $this->bands - 1 - $offset));
1265+
$offset = $this->bands - $n_right;
1266+
1267+
$components = [];
1268+
if ($n_left > 0) {
1269+
$components[] = $this->extract_band(0, ["n" => $n_left]);
1270+
}
1271+
$components[] = $value;
1272+
if ($n_right) {
1273+
$components[] = $this->extract_band($offset, ["n" => $n_right]);
1274+
}
1275+
1276+
$head = array_shift($components);
1277+
$this->image = $head->bandjoin($components)->image;
11991278
}
12001279

12011280
/**
1202-
* Our ArrayAccess interface ... we allow [] to get band.
1281+
* Remove a band from an image.
12031282
*
1204-
* @param mixed $offset The index to remove.
1283+
* @param int $offset The index to remove.
12051284
*
1206-
* @return Image the reduced image.
1285+
* @return void
12071286
*/
1208-
public function offsetUnset($offset): Image
1287+
public function offsetUnset($offset): void
12091288
{
1210-
throw new \BadMethodCallException('Image::offsetUnset: not implemented');
1289+
if (is_int($offset) and $offset >= 0 and $offset < $this->bands) {
1290+
$components = [];
1291+
if ($offset > 0) {
1292+
$components[] = $this->extract_band(0, ["n" => $offset]);
1293+
}
1294+
if ($offset < $this->bands - 1) {
1295+
$components[] = $this->extract_band(
1296+
$offset + 1,
1297+
["n" => $this->bands - 1 - $offset]
1298+
);
1299+
}
1300+
1301+
$head = array_shift($components);
1302+
if ($components != null) {
1303+
$this->image = $head->bandjoin($components)->image;
1304+
} else {
1305+
$this->image = $head->image;
1306+
}
1307+
}
12111308
}
12121309

12131310
/**

tests/shortcuts.php

+86
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,92 @@ public function testVipsIndex()
236236
$this->assertEquals($vips->bands, 1);
237237
}
238238

239+
public function testOffsetSet()
240+
{
241+
$base = Vips\Image::newFromArray([1, 2, 3]);
242+
$image = $base->bandjoin([$base->add(1), $base->add(2)]);
243+
244+
// replace band with image
245+
$test = $image->copy();
246+
$test[1] = $base;
247+
$this->assertEquals($test->bands, 3);
248+
$this->assertEquals($test[0]->avg(), 2);
249+
$this->assertEquals($test[1]->avg(), 2);
250+
$this->assertEquals($test[2]->avg(), 4);
251+
252+
// replace band with constant
253+
$test = $image->copy();
254+
$test[1] = 12;
255+
$this->assertEquals($test->bands, 3);
256+
$this->assertEquals($test[0]->avg(), 2);
257+
$this->assertEquals($test[1]->avg(), 12);
258+
$this->assertEquals($test[2]->avg(), 4);
259+
260+
// replace band with array
261+
$test = $image->copy();
262+
$test[1] = [12, 13];
263+
$this->assertEquals($test->bands, 4);
264+
$this->assertEquals($test[0]->avg(), 2);
265+
$this->assertEquals($test[1]->avg(), 12);
266+
$this->assertEquals($test[2]->avg(), 13);
267+
$this->assertEquals($test[3]->avg(), 4);
268+
269+
// insert at start
270+
$test = $image->copy();
271+
$test[-1] = 12;
272+
$this->assertEquals($test->bands, 4);
273+
$this->assertEquals($test[0]->avg(), 12);
274+
$this->assertEquals($test[1]->avg(), 2);
275+
$this->assertEquals($test[2]->avg(), 3);
276+
$this->assertEquals($test[3]->avg(), 4);
277+
278+
// append at end
279+
$test = $image->copy();
280+
$test[] = 12;
281+
$this->assertEquals($test->bands, 4);
282+
$this->assertEquals($test[0]->avg(), 2);
283+
$this->assertEquals($test[1]->avg(), 3);
284+
$this->assertEquals($test[2]->avg(), 4);
285+
$this->assertEquals($test[3]->avg(), 12);
286+
287+
}
288+
289+
public function testOffsetUnset()
290+
{
291+
$base = Vips\Image::newFromArray([1, 2, 3]);
292+
$image = $base->bandjoin([$base->add(1), $base->add(2)]);
293+
294+
// remove middle
295+
$test = $image->copy();
296+
unset($test[1]);
297+
$this->assertEquals($test->bands, 2);
298+
$this->assertEquals($test[0]->avg(), 2);
299+
$this->assertEquals($test[1]->avg(), 4);
300+
301+
// remove first
302+
$test = $image->copy();
303+
unset($test[0]);
304+
$this->assertEquals($test->bands, 2);
305+
$this->assertEquals($test[0]->avg(), 3);
306+
$this->assertEquals($test[1]->avg(), 4);
307+
308+
309+
// remove last
310+
$test = $image->copy();
311+
unset($test[2]);
312+
$this->assertEquals($test->bands, 2);
313+
$this->assertEquals($test[0]->avg(), 2);
314+
$this->assertEquals($test[1]->avg(), 3);
315+
316+
// remove outside range
317+
$test = $image->copy();
318+
unset($test[12]);
319+
$this->assertEquals($test->bands, 3);
320+
$this->assertEquals($test[0]->avg(), 2);
321+
$this->assertEquals($test[1]->avg(), 3);
322+
$this->assertEquals($test[2]->avg(), 4);
323+
324+
}
239325
}
240326

241327
/*

0 commit comments

Comments
 (0)