diff --git a/src/doc/oiiotool.rst b/src/doc/oiiotool.rst index ab1289fc72..bb790c3208 100644 --- a/src/doc/oiiotool.rst +++ b/src/doc/oiiotool.rst @@ -1836,8 +1836,12 @@ Writing images full data window of the image. The rectangle can be specified using either of these image geometry notations: + *width* x *height* + *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + *xmin,ymin,xmax,ymax* - `:allsubimages=` *int* @@ -2226,14 +2230,17 @@ current top image. Set the pixel data window origin, essentially translating the existing pixel data window to a different position on the image plane. - The new data origin is in the form:: + The new data origin is in either of these forms:: [+-]x[+-]y + x,y + Examples:: --origin +20+10 x=20, y=10 --origin +0-40 x=0, y=-40 + --origin 0,-40 x=0, y=-40 using comma notation .. option:: --originoffset @@ -2243,12 +2250,15 @@ current top image. The offset is in the form:: [+-]x[+-]y + + x,y Examples:: # Assuming the old origin was +100+20... --originoffset +20+10 # new x=120, y=30 --originoffset +0-40 # new x=100, y=-20 + --originoffset 0,-40 # new x=100, y=-20 comma notation .. option:: --fullsize @@ -2256,8 +2266,12 @@ current top image. Set the display/full window size and/or offset. The size is in the form + *width* x *height* + *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + If either the offset or resolution is omitted, it will remain unchanged. @@ -2266,7 +2280,9 @@ current top image. ============================ ============================================ `--fullsize 1920x1080` resolution w=1920, h=1080, offset unchanged `--fullsize -20-30` resolution unchanged, x=-20, y=-30 + `--fullsize -20,-30` resolution unchanged, x=-20, y=-30 `--fullsize 1024x768+100+0` resolution w=1024, h=768, offset x=100, y=0 + `--fullsize 1024x768,100,0` resolution w=1024, h=768, offset x=100, y=0 ============================ ============================================ @@ -2471,8 +2487,12 @@ current top image. The *size* is in the form + *width* x *height* + *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + If the offset is omitted, it will be x=0, y=0. Optional appended arguments include: @@ -2483,6 +2503,7 @@ current top image. --create 1920x1080 3 # RGB with w=1920, h=1080, x=0, y=0 --create 1024x768+100+0 4 # RGBA with w=1024, h=768, x=100, y=0 + --create 1024x768,100,0 4 # same, usign comma notation --create:type=uint8 1920x1080 3 # RGB, store internally as uint8 @@ -2494,8 +2515,12 @@ current top image. The *size* is in the form + *width* x *height* + *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + If the offset is omitted, it will be x=0, y=0. Optional appended arguments include: @@ -3117,9 +3142,9 @@ current top image. "background" -- and uses the pixels of the foreground to replace those of the background, with foreground pixel (0,0) being pasted to the background at the *location* specified (expressed as `+xpos+ypos`, e.g., - `+100+50`, or of course using `-` for negative offsets). Only pixels - within the actual data region of the foreground image are pasted in this - manner. + `+100+50`, or of course using `-` for negative offsets, or alternately + with a comma as `xpos,ypos`). Only pixels within the actual data region of + the foreground image are pasted in this manner. Note that if location is +0+0, the foreground image's data region will be copied to its same position in the background image (this is useful @@ -3153,6 +3178,9 @@ current top image. # Merge many non-overlapping "tiles" into one combined image oiiotool img*.exr -paste:mergeroi=1:all=1 +0+0 -o combined.exr + # Comma-separated notation is fine also + oiiotool fg.exr bg.exr -paste 100,100 -o out.exr + .. option:: --pastemeta @@ -3426,6 +3454,8 @@ current top image. *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + or *xmin,ymin,xmax,ymax* @@ -3482,6 +3512,8 @@ current top image. *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + or *xmin,ymin,xmax,ymax* @@ -3510,6 +3542,8 @@ current top image. *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + *xmin,ymin,xmax,ymax* *wscale% x hscale%* @@ -3543,6 +3577,8 @@ current top image. *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + *xmin,ymin,xmax,ymax* *scale%* @@ -3618,6 +3654,8 @@ current top image. *width* x *height* [+-] *xoffset* [+-] *yoffset* + *width* x *height* , *xoffset* , *yoffset* + Optional appended modifiers include: - `filter=` *name* : Filter name. The default is `blackman-harris` when diff --git a/src/oiiotool/oiiotool.cpp b/src/oiiotool/oiiotool.cpp index fbf8d5cbc7..dba88a32e9 100644 --- a/src/oiiotool/oiiotool.cpp +++ b/src/oiiotool/oiiotool.cpp @@ -286,22 +286,39 @@ template static bool scan_offset(string_view str, T& x, T& y) { - return Strutil::parse_value(str, x) - && (str.size() && (str[0] == '+' || str[0] == '-')) - && Strutil::parse_value(str, y); + string_view orig = str; + if (Strutil::parse_value(str, x) + && (str.size() && (str[0] == '+' || str[0] == '-')) + && Strutil::parse_value(str, y)) + return true; + str = orig; + if (Strutil::parse_value(str, x) && Strutil::parse_char(str, ',') + && Strutil::parse_value(str, y)) + return true; + return false; } + template static bool scan_res_offset(string_view str, T& w, T& h, T& x, T& y) { - return Strutil::parse_value(str, w) && Strutil::parse_char(str, 'x') - && Strutil::parse_value(str, h) - && (str.size() && (str[0] == '+' || str[0] == '-')) - && Strutil::parse_value(str, x) - && (str.size() && (str[0] == '+' || str[0] == '-')) - && Strutil::parse_value(str, y); // NOSONAR + string_view orig = str; + if (Strutil::parse_value(str, w) && Strutil::parse_char(str, 'x') + && Strutil::parse_value(str, h) + && (str.size() && (str[0] == '+' || str[0] == '-')) + && Strutil::parse_value(str, x) + && (str.size() && (str[0] == '+' || str[0] == '-')) + && Strutil::parse_value(str, y)) + return true; + str = orig; + if (Strutil::parse_value(str, w) && Strutil::parse_char(str, 'x') + && Strutil::parse_value(str, h) && Strutil::parse_char(str, ',') + && Strutil::parse_value(str, x) && Strutil::parse_char(str, ',') + && Strutil::parse_value(str, y)) + return true; + return false; } @@ -968,7 +985,7 @@ adjust_output_options(string_view filename, ImageSpec& spec, if (tilesize.size()) { int x, y; // dummy vals for adjust_geometry ot.adjust_geometry("-o", requested_tilewidth, requested_tileheight, x, - y, tilesize.c_str(), false); + y, tilesize.c_str(), false, false); } bool requested_scanline = fileoptions.get_int("scanline", ot.output_scanline); @@ -1775,7 +1792,23 @@ unit_test_adjust_geometry(Oiiotool& ot) OIIO_CHECK_ASSERT( !ot.adjust_geometry("foo", w, h, x, y, "10x20+100+200", true, false)); - // res + // geom with comma notation for origin + w = h = x = y = -42; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "10x20,100,200") + && x == 100 && y == 200 && w == 10 && h == 20); + w = h = x = y = -42; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "10x20,-100,-200") + && x == -100 && y == -200 && w == 10 && h == 20); + w = 100, h = 50, x = y = 0; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "20x0,100,200") + && x == 100 && y == 200 && w == 20 && h == 10); + w = 100, h = 50, x = y = 0; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "0x20,100,200") + && x == 100 && y == 200 && w == 40 && h == 20); + OIIO_CHECK_ASSERT( + !ot.adjust_geometry("foo", w, h, x, y, "10x20+100+200", true, false)); + + // res only w = h = x = y = -42; OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "10x20") && x == -42 && y == -42 && w == 10 && h == 20); @@ -1807,6 +1840,23 @@ unit_test_adjust_geometry(Oiiotool& ot) w = h = x = y = -42; OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "+100+200") && x == 100 && y == 200 && w == -42 && h == -42); + w = h = x = y = -42; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "+100-200") + && x == 100 && y == -200 && w == -42 && h == -42); + w = h = x = y = -42; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "-100+200") + && x == -100 && y == 200 && w == -42 && h == -42); + + // offset with comma notation + w = h = x = y = -42; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "100,200") + && x == 100 && y == 200 && w == -42 && h == -42); + w = h = x = y = -42; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "100,-200") + && x == 100 && y == -200 && w == -42 && h == -42); + w = h = x = y = -42; + OIIO_CHECK_ASSERT(ot.adjust_geometry("foo", w, h, x, y, "-100,200") + && x == -100 && y == 200 && w == -42 && h == -42); // scale by factor w = 640; diff --git a/testsuite/oiiotool-attribs/ref/out-jpeg9d.txt b/testsuite/oiiotool-attribs/ref/out-jpeg9d.txt index 04737c6557..6b7a99a93d 100644 --- a/testsuite/oiiotool-attribs/ref/out-jpeg9d.txt +++ b/testsuite/oiiotool-attribs/ref/out-jpeg9d.txt @@ -72,8 +72,14 @@ noattribs.jpg : 2048 x 1536, 3 channel, uint8 jpeg initial keywords= after adding, keywords=foo; bar after clearing, keywords= -after --origin, 64x64+10+20 -after --originoffset, 64x64+110+220 +initial, 64x64+1+1 + after --origin +10+20, 64x64+10+20 + after --origin -10-20, 64x64-10-20 + after --originoffset +100+200, 64x64+90+180 +initial, 64x64+1+1 + after --origin 10,20, 64x64+10+20 + after --origin -10,-20, 64x64-10-20 + after --originoffset 100,200, 64x64+90+180 Reading attrib2.exr attrib2.exr : 64 x 64, 3 channel, half openexr 2 subimages: 64x64 [h,h,h], 64x64 [h,h,h] diff --git a/testsuite/oiiotool-attribs/ref/out.txt b/testsuite/oiiotool-attribs/ref/out.txt index b51b530a60..4aaf76d00b 100644 --- a/testsuite/oiiotool-attribs/ref/out.txt +++ b/testsuite/oiiotool-attribs/ref/out.txt @@ -72,8 +72,14 @@ noattribs.jpg : 2048 x 1536, 3 channel, uint8 jpeg initial keywords= after adding, keywords=foo; bar after clearing, keywords= -after --origin, 64x64+10+20 -after --originoffset, 64x64+110+220 +initial, 64x64+1+1 + after --origin +10+20, 64x64+10+20 + after --origin -10-20, 64x64-10-20 + after --originoffset +100+200, 64x64+90+180 +initial, 64x64+1+1 + after --origin 10,20, 64x64+10+20 + after --origin -10,-20, 64x64-10-20 + after --originoffset 100,200, 64x64+90+180 Reading attrib2.exr attrib2.exr : 64 x 64, 3 channel, half openexr 2 subimages: 64x64 [h,h,h], 64x64 [h,h,h] diff --git a/testsuite/oiiotool-attribs/run.py b/testsuite/oiiotool-attribs/run.py index fb6449afc0..ec29f405d3 100755 --- a/testsuite/oiiotool-attribs/run.py +++ b/testsuite/oiiotool-attribs/run.py @@ -35,11 +35,25 @@ "--clear-keywords " + "--echo \"after clearing, keywords={TOP[keywords]}\" ") -# Test --origin and --originoffset -command += oiiotool ('--create 64x64 3 --origin +10+20 ' + - '--echo "after --origin, {TOP.geom}" ' + +# Test --origin and --originoffset with "X11 geometry" syntax +command += oiiotool ('--create 64x64+1+1 3 ' + + '--echo "initial, {TOP.geom}" ' + + '--origin +10+20 ' + + '--echo " after --origin +10+20, {TOP.geom}" ' + + '--origin -10-20 ' + + '--echo " after --origin -10-20, {TOP.geom}" ' + '--originoffset +100+200 ' + - '--echo "after --originoffset, {TOP.geom}"') + '--echo " after --originoffset +100+200, {TOP.geom}"') + +# Test --origin and --originoffset with "comma" syntax +command += oiiotool ('--create 64x64,1,1 3 ' + + '--echo "initial, {TOP.geom}" ' + + '--origin 10,20 ' + + '--echo " after --origin 10,20, {TOP.geom}" ' + + '--origin -10,-20 ' + + '--echo " after --origin -10,-20, {TOP.geom}" ' + + '--originoffset 100,200 ' + + '--echo " after --originoffset 100,200, {TOP.geom}"') # Test adding and erasing attribs to multiple subimages command += oiiotool ("--create 64x64 3 -dup --siappend " +