Skip to content

Commit 64e0b40

Browse files
lgritzCopilot
andauthored
feat(oiiotool): flexible offset notation with commas (#5209)
Fixes #5207 Sometimes the `WxH+X+Y` and `+X+Y` notation (borrowed from X11!) can be a little awkward. This patch extends the accepted syntax to allow commas to separate the offset components, for example also accepting `WxY,X,Y` and `X,Y`. This affects the commands --create --crop --cut --fit --fullsize --origin --originoffset --paste --pattern --printstats --resize --------- Signed-off-by: Larry Gritz <lg@larrygritz.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 0078f36 commit 64e0b40

5 files changed

Lines changed: 137 additions & 23 deletions

File tree

src/doc/oiiotool.rst

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,8 +1836,12 @@ Writing images
18361836
full data window of the image. The rectangle can be specified using
18371837
either of these image geometry notations:
18381838

1839+
*width* x *height*
1840+
18391841
*width* x *height* [+-] *xoffset* [+-] *yoffset*
18401842

1843+
*width* x *height* , *xoffset* , *yoffset*
1844+
18411845
*xmin,ymin,xmax,ymax*
18421846

18431847
- `:allsubimages=` *int*
@@ -2226,14 +2230,17 @@ current top image.
22262230

22272231
Set the pixel data window origin, essentially translating the existing
22282232
pixel data window to a different position on the image plane.
2229-
The new data origin is in the form::
2233+
The new data origin is in either of these forms::
22302234
22312235
[+-]x[+-]y
22322236

2237+
x,y
2238+
22332239
Examples::
22342240

22352241
--origin +20+10 x=20, y=10
22362242
--origin +0-40 x=0, y=-40
2243+
--origin 0,-40 x=0, y=-40 using comma notation
22372244

22382245

22392246
.. option:: --originoffset <offset>
@@ -2243,21 +2250,28 @@ current top image.
22432250
The offset is in the form::
22442251
22452252
[+-]x[+-]y
2253+
2254+
x,y
22462255

22472256
Examples::
22482257

22492258
# Assuming the old origin was +100+20...
22502259
--originoffset +20+10 # new x=120, y=30
22512260
--originoffset +0-40 # new x=100, y=-20
2261+
--originoffset 0,-40 # new x=100, y=-20 comma notation
22522262

22532263

22542264
.. option:: --fullsize <size>
22552265

22562266
Set the display/full window size and/or offset. The size is in the
22572267
form
22582268

2269+
*width* x *height*
2270+
22592271
*width* x *height* [+-] *xoffset* [+-] *yoffset*
22602272

2273+
*width* x *height* , *xoffset* , *yoffset*
2274+
22612275
If either the offset or resolution is omitted, it will remain
22622276
unchanged.
22632277

@@ -2266,7 +2280,9 @@ current top image.
22662280
============================ ============================================
22672281
`--fullsize 1920x1080` resolution w=1920, h=1080, offset unchanged
22682282
`--fullsize -20-30` resolution unchanged, x=-20, y=-30
2283+
`--fullsize -20,-30` resolution unchanged, x=-20, y=-30
22692284
`--fullsize 1024x768+100+0` resolution w=1024, h=768, offset x=100, y=0
2285+
`--fullsize 1024x768,100,0` resolution w=1024, h=768, offset x=100, y=0
22702286
============================ ============================================
22712287

22722288

@@ -2471,8 +2487,12 @@ current top image.
24712487

24722488
The *size* is in the form
24732489

2490+
*width* x *height*
2491+
24742492
*width* x *height* [+-] *xoffset* [+-] *yoffset*
24752493

2494+
*width* x *height* , *xoffset* , *yoffset*
2495+
24762496
If the offset is omitted, it will be x=0, y=0. Optional appended
24772497
arguments include:
24782498

@@ -2483,6 +2503,7 @@ current top image.
24832503

24842504
--create 1920x1080 3 # RGB with w=1920, h=1080, x=0, y=0
24852505
--create 1024x768+100+0 4 # RGBA with w=1024, h=768, x=100, y=0
2506+
--create 1024x768,100,0 4 # same, using comma notation
24862507
--create:type=uint8 1920x1080 3 # RGB, store internally as uint8
24872508

24882509

@@ -2494,8 +2515,12 @@ current top image.
24942515

24952516
The *size* is in the form
24962517

2518+
*width* x *height*
2519+
24972520
*width* x *height* [+-] *xoffset* [+-] *yoffset*
24982521

2522+
*width* x *height* , *xoffset* , *yoffset*
2523+
24992524
If the offset is omitted, it will be x=0, y=0. Optional appended
25002525
arguments include:
25012526

@@ -3117,9 +3142,9 @@ current top image.
31173142
"background" -- and uses the pixels of the foreground to replace those
31183143
of the background, with foreground pixel (0,0) being pasted to the
31193144
background at the *location* specified (expressed as `+xpos+ypos`, e.g.,
3120-
`+100+50`, or of course using `-` for negative offsets). Only pixels
3121-
within the actual data region of the foreground image are pasted in this
3122-
manner.
3145+
`+100+50`, or of course using `-` for negative offsets, or alternately
3146+
with a comma as `xpos,ypos`). Only pixels within the actual data region of
3147+
the foreground image are pasted in this manner.
31233148

31243149
Note that if location is +0+0, the foreground image's data region will
31253150
be copied to its same position in the background image (this is useful
@@ -3153,6 +3178,9 @@ current top image.
31533178
# Merge many non-overlapping "tiles" into one combined image
31543179
oiiotool img*.exr -paste:mergeroi=1:all=1 +0+0 -o combined.exr
31553180

3181+
# Comma-separated notation is fine also
3182+
oiiotool fg.exr bg.exr -paste 100,100 -o out.exr
3183+
31563184

31573185
.. option:: --pastemeta <location>
31583186

@@ -3426,6 +3454,8 @@ current top image.
34263454

34273455
*width* x *height* [+-] *xoffset* [+-] *yoffset*
34283456

3457+
*width* x *height* , *xoffset* , *yoffset*
3458+
34293459
or
34303460

34313461
*xmin,ymin,xmax,ymax*
@@ -3482,6 +3512,8 @@ current top image.
34823512

34833513
*width* x *height* [+-] *xoffset* [+-] *yoffset*
34843514

3515+
*width* x *height* , *xoffset* , *yoffset*
3516+
34853517
or
34863518

34873519
*xmin,ymin,xmax,ymax*
@@ -3510,6 +3542,8 @@ current top image.
35103542

35113543
*width* x *height* [+-] *xoffset* [+-] *yoffset*
35123544

3545+
*width* x *height* , *xoffset* , *yoffset*
3546+
35133547
*xmin,ymin,xmax,ymax*
35143548

35153549
*wscale% x hscale%*
@@ -3543,6 +3577,8 @@ current top image.
35433577

35443578
*width* x *height* [+-] *xoffset* [+-] *yoffset*
35453579

3580+
*width* x *height* , *xoffset* , *yoffset*
3581+
35463582
*xmin,ymin,xmax,ymax*
35473583

35483584
*scale%*
@@ -3618,6 +3654,8 @@ current top image.
36183654

36193655
*width* x *height* [+-] *xoffset* [+-] *yoffset*
36203656

3657+
*width* x *height* , *xoffset* , *yoffset*
3658+
36213659
Optional appended modifiers include:
36223660

36233661
- `filter=` *name* : Filter name. The default is `blackman-harris` when

src/oiiotool/oiiotool.cpp

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -286,22 +286,39 @@ template<typename T>
286286
static bool
287287
scan_offset(string_view str, T& x, T& y)
288288
{
289-
return Strutil::parse_value(str, x)
290-
&& (str.size() && (str[0] == '+' || str[0] == '-'))
291-
&& Strutil::parse_value(str, y);
289+
string_view orig = str;
290+
if (Strutil::parse_value(str, x)
291+
&& (str.size() && (str[0] == '+' || str[0] == '-'))
292+
&& Strutil::parse_value(str, y))
293+
return true;
294+
str = orig;
295+
if (Strutil::parse_value(str, x) && Strutil::parse_char(str, ',')
296+
&& Strutil::parse_value(str, y))
297+
return true;
298+
return false;
292299
}
293300

294301

302+
295303
template<typename T>
296304
static bool
297305
scan_res_offset(string_view str, T& w, T& h, T& x, T& y)
298306
{
299-
return Strutil::parse_value(str, w) && Strutil::parse_char(str, 'x')
300-
&& Strutil::parse_value(str, h)
301-
&& (str.size() && (str[0] == '+' || str[0] == '-'))
302-
&& Strutil::parse_value(str, x)
303-
&& (str.size() && (str[0] == '+' || str[0] == '-'))
304-
&& Strutil::parse_value(str, y); // NOSONAR
307+
string_view orig = str;
308+
if (Strutil::parse_value(str, w) && Strutil::parse_char(str, 'x')
309+
&& Strutil::parse_value(str, h)
310+
&& (str.size() && (str[0] == '+' || str[0] == '-'))
311+
&& Strutil::parse_value(str, x)
312+
&& (str.size() && (str[0] == '+' || str[0] == '-'))
313+
&& Strutil::parse_value(str, y))
314+
return true;
315+
str = orig;
316+
if (Strutil::parse_value(str, w) && Strutil::parse_char(str, 'x')
317+
&& Strutil::parse_value(str, h) && Strutil::parse_char(str, ',')
318+
&& Strutil::parse_value(str, x) && Strutil::parse_char(str, ',')
319+
&& Strutil::parse_value(str, y))
320+
return true;
321+
return false;
305322
}
306323

307324

@@ -968,7 +985,7 @@ adjust_output_options(string_view filename, ImageSpec& spec,
968985
if (tilesize.size()) {
969986
int x, y; // dummy vals for adjust_geometry
970987
ot.adjust_geometry("-o", requested_tilewidth, requested_tileheight, x,
971-
y, tilesize.c_str(), false);
988+
y, tilesize.c_str(), false, false);
972989
}
973990
bool requested_scanline = fileoptions.get_int("scanline",
974991
ot.output_scanline);
@@ -1775,7 +1792,23 @@ unit_test_adjust_geometry(Oiiotool& ot)
17751792
OIIO_CHECK_ASSERT(
17761793
!ot.adjust_geometry("foo", w, h, x, y, "10x20+100+200", true, false));
17771794

1778-
// res
1795+
// geom with comma notation for origin
1796+
w = h = x = y = -42;
1797+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "10x20,100,200")
1798+
&& x == 100 && y == 200 && w == 10 && h == 20);
1799+
w = h = x = y = -42;
1800+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "10x20,-100,-200")
1801+
&& x == -100 && y == -200 && w == 10 && h == 20);
1802+
w = 100, h = 50, x = y = 0;
1803+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "20x0,100,200")
1804+
&& x == 100 && y == 200 && w == 20 && h == 10);
1805+
w = 100, h = 50, x = y = 0;
1806+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "0x20,100,200")
1807+
&& x == 100 && y == 200 && w == 40 && h == 20);
1808+
OIIO_CHECK_ASSERT(
1809+
!ot.adjust_geometry("foo", w, h, x, y, "10x20+100+200", true, false));
1810+
1811+
// res only
17791812
w = h = x = y = -42;
17801813
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "10x20") && x == -42
17811814
&& y == -42 && w == 10 && h == 20);
@@ -1807,6 +1840,23 @@ unit_test_adjust_geometry(Oiiotool& ot)
18071840
w = h = x = y = -42;
18081841
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "+100+200")
18091842
&& x == 100 && y == 200 && w == -42 && h == -42);
1843+
w = h = x = y = -42;
1844+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "+100-200")
1845+
&& x == 100 && y == -200 && w == -42 && h == -42);
1846+
w = h = x = y = -42;
1847+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "-100+200")
1848+
&& x == -100 && y == 200 && w == -42 && h == -42);
1849+
1850+
// offset with comma notation
1851+
w = h = x = y = -42;
1852+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "100,200")
1853+
&& x == 100 && y == 200 && w == -42 && h == -42);
1854+
w = h = x = y = -42;
1855+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "100,-200")
1856+
&& x == 100 && y == -200 && w == -42 && h == -42);
1857+
w = h = x = y = -42;
1858+
OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "-100,200")
1859+
&& x == -100 && y == 200 && w == -42 && h == -42);
18101860

18111861
// scale by factor
18121862
w = 640;

testsuite/oiiotool-attribs/ref/out-jpeg9d.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,14 @@ noattribs.jpg : 2048 x 1536, 3 channel, uint8 jpeg
7272
initial keywords=
7373
after adding, keywords=foo; bar
7474
after clearing, keywords=
75-
after --origin, 64x64+10+20
76-
after --originoffset, 64x64+110+220
75+
initial, 64x64+1+1
76+
after --origin +10+20, 64x64+10+20
77+
after --origin -10-20, 64x64-10-20
78+
after --originoffset +100+200, 64x64+90+180
79+
initial, 64x64+1+1
80+
after --origin 10,20, 64x64+10+20
81+
after --origin -10,-20, 64x64-10-20
82+
after --originoffset 100,200, 64x64+90+180
7783
Reading attrib2.exr
7884
attrib2.exr : 64 x 64, 3 channel, half openexr
7985
2 subimages: 64x64 [h,h,h], 64x64 [h,h,h]

testsuite/oiiotool-attribs/ref/out.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,14 @@ noattribs.jpg : 2048 x 1536, 3 channel, uint8 jpeg
7272
initial keywords=
7373
after adding, keywords=foo; bar
7474
after clearing, keywords=
75-
after --origin, 64x64+10+20
76-
after --originoffset, 64x64+110+220
75+
initial, 64x64+1+1
76+
after --origin +10+20, 64x64+10+20
77+
after --origin -10-20, 64x64-10-20
78+
after --originoffset +100+200, 64x64+90+180
79+
initial, 64x64+1+1
80+
after --origin 10,20, 64x64+10+20
81+
after --origin -10,-20, 64x64-10-20
82+
after --originoffset 100,200, 64x64+90+180
7783
Reading attrib2.exr
7884
attrib2.exr : 64 x 64, 3 channel, half openexr
7985
2 subimages: 64x64 [h,h,h], 64x64 [h,h,h]

testsuite/oiiotool-attribs/run.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,25 @@
3535
"--clear-keywords " +
3636
"--echo \"after clearing, keywords={TOP[keywords]}\" ")
3737

38-
# Test --origin and --originoffset
39-
command += oiiotool ('--create 64x64 3 --origin +10+20 ' +
40-
'--echo "after --origin, {TOP.geom}" ' +
38+
# Test --origin and --originoffset with "X11 geometry" syntax
39+
command += oiiotool ('--create 64x64+1+1 3 ' +
40+
'--echo "initial, {TOP.geom}" ' +
41+
'--origin +10+20 ' +
42+
'--echo " after --origin +10+20, {TOP.geom}" ' +
43+
'--origin -10-20 ' +
44+
'--echo " after --origin -10-20, {TOP.geom}" ' +
4145
'--originoffset +100+200 ' +
42-
'--echo "after --originoffset, {TOP.geom}"')
46+
'--echo " after --originoffset +100+200, {TOP.geom}"')
47+
48+
# Test --origin and --originoffset with "comma" syntax
49+
command += oiiotool ('--create 64x64,1,1 3 ' +
50+
'--echo "initial, {TOP.geom}" ' +
51+
'--origin 10,20 ' +
52+
'--echo " after --origin 10,20, {TOP.geom}" ' +
53+
'--origin -10,-20 ' +
54+
'--echo " after --origin -10,-20, {TOP.geom}" ' +
55+
'--originoffset 100,200 ' +
56+
'--echo " after --originoffset 100,200, {TOP.geom}"')
4357

4458
# Test adding and erasing attribs to multiple subimages
4559
command += oiiotool ("--create 64x64 3 -dup --siappend " +

0 commit comments

Comments
 (0)