Skip to content

Commit 2325227

Browse files
committed
Merge branch 'main' of github.com:AcademySoftwareFoundation/OpenImageIO into 4632_rust_bindings
Signed-off-by: Scott Wilson <scott@propersquid.com>
2 parents e2327a8 + 96c536e commit 2325227

File tree

18 files changed

+365
-62
lines changed

18 files changed

+365
-62
lines changed

.github/workflows/ci.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,12 +605,21 @@ jobs:
605605
include:
606606
- desc: Windows-2019 VS2019
607607
runner: windows-2019
608+
nametag: windows-2019
608609
vsver: 2019
609610
generator: "Visual Studio 16 2019"
610611
python_ver: "3.9"
611612
setenvs: export OPENIMAGEIO_PYTHON_LOAD_DLLS_FROM_PATH=1
612613
- desc: Windows-2022 VS2022
613614
runner: windows-2022
615+
nametag: windows-2022
616+
vsver: 2022
617+
generator: "Visual Studio 17 2022"
618+
python_ver: "3.9"
619+
setenvs: export OPENIMAGEIO_PYTHON_LOAD_DLLS_FROM_PATH=1
620+
- desc: Windows-2025 VS2022
621+
runner: windows-2025
622+
nametag: windows-2025
614623
vsver: 2022
615624
generator: "Visual Studio 17 2022"
616625
python_ver: "3.9"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ gastest.o
2323
/*.exr
2424
/*.tif
2525
/*.jpg
26+
/*.png
2627
/*.jxl
2728
/*.tx
2829
/*.log
401 KB
Loading

src/doc/pythonbindings.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4260,3 +4260,39 @@ add an alpha channel that is 1 everywhere**
42604260
out.open ("multipart.exr", specs[s], "AppendSubimage")
42614261
bufs[s].write (out)
42624262
out.close ()
4263+
4264+
4265+
4266+
|
4267+
4268+
**Running OpenImageIO in Jupyter Notebooks and displaying ImageBuf**
4269+
4270+
4271+
Like any other Python package, OpenImageIO can be used in `Jupyter notebooks <https://jupyter.org/install>`_.
4272+
The ImageBuf objects support getting displayed inline within notebooks.
4273+
4274+
.. image:: figures/imagebuf-notebook-demo.png
4275+
4276+
.. warning::
4277+
4278+
Currently, ImageBuf objects get displayed as **uint8 PNGs** inside of notebooks.
4279+
ImageBuf objects that store images with higher bit depths get dithered to account for this.
4280+
Keep in mind that directly saving the inline image to disk will not preserve the original image within the ImageBuf.
4281+
4282+
4283+
Running a Local Jupyter Notebook:
4284+
4285+
If you want to run a local Jupyter notebook with OpenImageIO, you can do so from within the Python environment in which you have installed OpenImageIO.
4286+
4287+
.. code-block:: bash
4288+
4289+
pip install jupyterlab
4290+
jupyter lab
4291+
4292+
Alternatively, if you prefer using `uv <https://github.com/astral-sh/uv>`_, you can run the following command:
4293+
4294+
.. code-block:: bash
4295+
4296+
uv run --with jupyter jupyter lab
4297+
4298+

src/include/OpenImageIO/typedesc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,9 @@ struct formatter<OIIO::TypeDesc> {
616616
{
617617
// Get the presentation type, if any. Required to be 's'.
618618
auto it = ctx.begin(), end = ctx.end();
619+
// Skip any width specifier. Remember that this is only for old
620+
// versions of fmt, so just don't sweat it.
621+
while (it != end && isdigit(*it)) ++it;
619622
if (it != end && (*it == 's')) ++it;
620623
// Check if reached the end of the range:
621624
if (it != end && *it != '}')

src/iv/imageviewer.cpp

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -854,14 +854,11 @@ ImageViewer::open()
854854
if (filename.empty())
855855
continue;
856856
add_image(filename);
857-
// int n = m_images.size()-1;
858-
// IvImage *newimage = m_images[n];
859-
// newimage->read_iv (0, false, image_progress_callback, this);
860857
}
858+
861859
if (old_lastimage >= 0) {
862860
// Otherwise, add_image already did this for us.
863861
current_image(old_lastimage + 1);
864-
fitWindowToImage(true, true);
865862
}
866863
}
867864

@@ -887,7 +884,6 @@ ImageViewer::openRecentFile()
887884
if (m_images.size() > 1) {
888885
// Otherwise, add_image already did this for us.
889886
current_image(m_images.size() - 1);
890-
fitWindowToImage(true, true);
891887
}
892888
}
893889
}
@@ -2009,67 +2005,59 @@ ImageViewer::print()
20092005
}
20102006

20112007

2012-
20132008
void
2014-
ImageViewer::zoomIn()
2009+
ImageViewer::zoomIn(bool smooth)
20152010
{
20162011
IvImage* img = cur();
20172012
if (!img)
20182013
return;
2019-
if (zoom() >= 64)
2014+
2015+
float current_zoom = zoom();
2016+
if (current_zoom >= 64)
20202017
return;
2021-
float oldzoom = zoom();
2022-
float newzoom = ceil2f(oldzoom);
20232018

2024-
float xc, yc; // Center view position
2025-
glwin->get_center(xc, yc);
2026-
int xm, ym; // Mouse position
2027-
glwin->get_focus_image_pixel(xm, ym);
2028-
float xoffset = xc - xm;
2029-
float yoffset = yc - ym;
2030-
float maxzoomratio = std::max(oldzoom / newzoom, newzoom / oldzoom);
2031-
int nsteps = (int)OIIO::clamp(20 * (maxzoomratio - 1), 2.0f, 10.0f);
2032-
for (int i = 1; i <= nsteps; ++i) {
2033-
float a = (float)i / (float)nsteps; // Interpolation amount
2034-
float z = OIIO::lerp(oldzoom, newzoom, a);
2035-
float zoomratio = z / oldzoom;
2036-
view(xm + xoffset / zoomratio, ym + yoffset / zoomratio, z, false);
2037-
if (i != nsteps) {
2038-
QApplication::processEvents();
2039-
Sysutil::usleep(1000000 / 4 / nsteps);
2040-
}
2041-
}
2019+
float newzoom = ceil2f(current_zoom);
20422020

2043-
fitImageToWindowAct->setChecked(false);
2021+
this->zoomToCursor(newzoom, smooth);
20442022
}
20452023

20462024

2047-
20482025
void
2049-
ImageViewer::zoomOut()
2026+
ImageViewer::zoomOut(bool smooth)
20502027
{
20512028
IvImage* img = cur();
20522029
if (!img)
20532030
return;
2054-
if (zoom() <= 1.0f / 64)
2031+
2032+
float current_zoom = zoom();
2033+
if (current_zoom <= 1.0f / 64)
20552034
return;
2035+
2036+
float newzoom = floor2f(current_zoom);
2037+
2038+
this->zoomToCursor(newzoom, smooth);
2039+
}
2040+
2041+
2042+
void
2043+
ImageViewer::zoomToCursor(float newzoom, bool smooth)
2044+
{
20562045
float oldzoom = zoom();
2057-
float newzoom = floor2f(oldzoom);
2058-
2059-
float xcpel, ycpel; // Center view position
2060-
glwin->get_center(xcpel, ycpel);
2061-
int xmpel, ympel; // Mouse position
2062-
glwin->get_focus_image_pixel(xmpel, ympel);
2063-
float xoffset = xcpel - xmpel;
2064-
float yoffset = ycpel - ympel;
2046+
float xc, yc; // Center view position
2047+
glwin->get_center(xc, yc);
2048+
int xm, ym; // Mouse position
2049+
glwin->get_focus_image_pixel(xm, ym);
2050+
float xoffset = xc - xm;
2051+
float yoffset = yc - ym;
2052+
20652053
float maxzoomratio = std::max(oldzoom / newzoom, newzoom / oldzoom);
2066-
int nsteps = (int)OIIO::clamp(20 * (maxzoomratio - 1), 2.0f, 10.0f);
2054+
int nsteps = smooth ? (int)OIIO::clamp(20 * (maxzoomratio - 1), 2.0f, 10.0f)
2055+
: 1;
20672056
for (int i = 1; i <= nsteps; ++i) {
20682057
float a = (float)i / (float)nsteps; // Interpolation amount
20692058
float z = OIIO::lerp(oldzoom, newzoom, a);
20702059
float zoomratio = z / oldzoom;
2071-
view(xmpel + xoffset / zoomratio, ympel + yoffset / zoomratio, z,
2072-
false);
2060+
view(xm + xoffset / zoomratio, ym + yoffset / zoomratio, z, false);
20732061
if (i != nsteps) {
20742062
QApplication::processEvents();
20752063
Sysutil::usleep(1000000 / 4 / nsteps);

src/iv/imageviewer.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -265,10 +265,12 @@ private slots:
265265
void moveToNewWindow(); ///< Split current image off as a new window
266266
void print(); ///< Print current image
267267
void deleteCurrentImage(); ///< Deleting displayed image
268-
void zoomIn(); ///< Zoom in to next power of 2
269-
void zoomOut(); ///< Zoom out to next power of 2
270-
void normalSize(); ///< Adjust zoom to 1:1
271-
void fitImageToWindow(); ///< Adjust zoom to fit window exactly
268+
void zoomIn(bool smooth = true); ///< Zoom in to next power of 2
269+
void zoomOut(bool smooth = true); ///< Zoom out to next power of 2
270+
void zoomToCursor(float newzoom,
271+
bool smooth = true); ///< Zoom to a specific level
272+
void normalSize(); ///< Adjust zoom to 1:1
273+
void fitImageToWindow(); ///< Adjust zoom to fit window exactly
272274
/// Resize window to fit image exactly. If zoomok is false, do not
273275
/// change the zoom, even to fit on screen. If minsize is true, do not
274276
/// resize smaller than default_width x default_height.

src/iv/ivgl.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,9 @@ IvGL::shadowed_text(float x, float y, float /*z*/, const std::string& s,
690690
* Paint on intermediate QImage, AA text on QOpenGLWidget based
691691
* QPaintDevice requires MSAA
692692
*/
693-
QImage t(size(), QImage::Format_ARGB32_Premultiplied);
693+
qreal dpr = devicePixelRatio();
694+
QImage t(size() * dpr, QImage::Format_ARGB32_Premultiplied);
695+
t.setDevicePixelRatio(dpr);
694696
t.fill(qRgba(0, 0, 0, 0));
695697
{
696698
QPainter painter(&t);
@@ -1218,13 +1220,13 @@ IvGL::mousePressEvent(QMouseEvent* event)
12181220
switch (event->button()) {
12191221
case Qt::LeftButton:
12201222
if (mousemode == ImageViewer::MouseModeZoom && !Alt)
1221-
m_viewer.zoomIn();
1223+
m_viewer.zoomIn(true); // Animated zoom for mouse clicks
12221224
else
12231225
m_dragging = true;
12241226
return;
12251227
case Qt::RightButton:
12261228
if (mousemode == ImageViewer::MouseModeZoom && !Alt)
1227-
m_viewer.zoomOut();
1229+
m_viewer.zoomOut(true); // Animated zoom for mouse clicks
12281230
else
12291231
m_dragging = true;
12301232
return;
@@ -1328,9 +1330,11 @@ IvGL::wheelEvent(QWheelEvent* event)
13281330
QPoint angdelta = event->angleDelta() / 8; // div by 8 to get degrees
13291331
if (abs(angdelta.y()) > abs(angdelta.x()) // predominantly vertical
13301332
&& abs(angdelta.y()) > 2) { // suppress tiny motions
1331-
float oldzoom = m_viewer.zoom();
1332-
float newzoom = (angdelta.y() > 0) ? ceil2f(oldzoom) : floor2f(oldzoom);
1333-
m_viewer.zoom(newzoom);
1333+
if (angdelta.y() > 0) {
1334+
m_viewer.zoomIn(false);
1335+
} else {
1336+
m_viewer.zoomOut(false);
1337+
}
13341338
event->accept();
13351339
}
13361340
// TODO: Update this to keep the zoom centered on the event .x, .y

src/iv/ivmain.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ main(int argc, char* argv[])
167167

168168
std::vector<std::string> validImages; // Vector to hold valid images
169169
for (auto& file : files) {
170-
std::string extension = Filesystem::extension(file).substr(
171-
1); // Remove the leading dot
170+
std::string extension
171+
= Filesystem::extension(file,
172+
false); // Remove the leading dot
173+
Strutil::to_lower(extension);
172174
if (std::find(extensionsVector.begin(), extensionsVector.end(),
173175
extension)
174176
!= extensionsVector.end()) {

src/libtexture/imagecache_pvt.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ class OIIO_API ImageCacheFile final : public RefCnt {
266266
/// precompute.
267267
struct LevelInfo {
268268
std::unique_ptr<ImageSpec> m_spec; ///< ImageSpec for the mip level,
269-
// only specified if different from nativespec
269+
// only specified if different from nativespec
270270
ImageSpec nativespec; ///< Native ImageSpec for the mip level
271271
mutable std::unique_ptr<float[]> polecolor; ///< Pole colors
272272
atomic_ll* tiles_read; ///< Bitfield for tiles read at least once
@@ -394,10 +394,10 @@ class OIIO_API ImageCacheFile final : public RefCnt {
394394
std::atomic<std::shared_ptr<ImageInput>> m_input;
395395
#else
396396
std::shared_ptr<ImageInput> m_input; ///< Open ImageInput, NULL if closed
397-
// Note that m_input, the shared pointer itself, is NOT safe to
398-
// access directly. ALWAYS retrieve its value with get_imageinput
399-
// (it's thread-safe to use that result) and set its value with
400-
// set_imageinput -- those are guaranteed thread-safe.
397+
// Note that m_input, the shared pointer itself, is NOT safe to
398+
// access directly. ALWAYS retrieve its value with get_imageinput
399+
// (it's thread-safe to use that result) and set its value with
400+
// set_imageinput -- those are guaranteed thread-safe.
401401
#endif
402402
std::vector<SubimageInfo> m_subimages; ///< Info on each subimage
403403
TexFormat m_texformat; ///< Which texture format

0 commit comments

Comments
 (0)