Merge branch 'master' into wip

This commit is contained in:
Matthias Koefferlein 2026-07-19 15:13:40 +02:00
commit b51187bf7c
17 changed files with 160 additions and 641 deletions

View File

@ -23,7 +23,7 @@ jobs:
outputs:
code: ${{ steps.filter.outputs.code == 'true' || github.event_name == 'release' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: dorny/paths-filter@v4
id: filter
if: github.event_name != 'release'
@ -78,7 +78,7 @@ jobs:
haskell: true
large-packages: true
- uses: hmarr/debug-action@v3
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: ccache
if: matrix.os != 'ubuntu-24.04-arm'
uses: hendrikmuhs/ccache-action@v1.2
@ -95,7 +95,7 @@ jobs:
mkdir -p $HOST_CCACHE_DIR
- name: Build wheels (ARM)
if: matrix.os == 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.4.1
uses: pypa/cibuildwheel@v4.1.0
env:
# override the default CentOS “yum install … ccache” and drop ccache
CIBW_BEFORE_ALL_LINUX: |
@ -110,7 +110,7 @@ jobs:
- name: Build wheels (all other platforms)
if: matrix.os != 'ubuntu-24.04-arm'
uses: pypa/cibuildwheel@v3.4.1
uses: pypa/cibuildwheel@v4.1.0
env:
CIBW_BUILD: ${{ matrix.cibuild }}
CIBW_ARCHS_MACOS: ${{ matrix.macos-arch }}
@ -139,7 +139,7 @@ jobs:
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Build SDist
run: pipx run build --sdist

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Cleanup
run: |

View File

@ -1,582 +0,0 @@
branches:
only:
- /v\d+\.\d+(\.\d+)?(\S*)?$/ # tag format: *v0.25(.8)(dev14)
- staging
- master
matrix:
include:
# python manylinux packages
- name: "cp39-cp39m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.9'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp39-cp39"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp38-cp38m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.8'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp38-cp38"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp37-cp37m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.7-dev'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp37-cp37m"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp36-cp36m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.6'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp36-cp36m"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp35-cp35m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '3.5'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp35-cp35m"
- DOCKER_BUILD=true
- TEST_IN_HOST=true
- MATRIX_EVAL=""
cache:
directories:
- ccache
- name: "cp27-cp27m-manylinux2014_x86_64.whl"
os: linux
sudo: true
language: python
python: '2.7'
services:
- docker
env:
- DOCKER_IMAGE="quay.io/pypa/manylinux2014_x86_64"
- PY_VERSION="cp27-cp27m"
- DOCKER_BUILD=true
- TEST_IN_HOST=false # travis's python 2.7 uses ucs4 (mu), so this test fails.
- MATRIX_EVAL=""
cache:
directories:
- ccache
# python 3 osx
# MacOS 10.15
# MacOS 10.15, Python 3.9
- name: "cp39-cp39m-macosx_10_15_x86_64.whl"
os: osx
osx_image: xcode12 # macOS 10.15
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.9
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.9/bin/python3'; alias pip='/usr/local/opt/python@3.9/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.15, Python 3.8
- name: "cp38-cp38m-macosx_10_15_x86_64.whl"
os: osx
osx_image: xcode12 # macOS 10.15
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.8
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.8/bin/python3'; alias pip='/usr/local/opt/python@3.8/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.15, Python 3.7
- name: "cp37-cp37m-macosx_10_15_x86_64.whl"
os: osx
osx_image: xcode12 # macOS 10.15
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.7
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.7/bin/python3'; alias pip='/usr/local/opt/python@3.7/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.14
# MacOS 10.14, Python 3.9
- name: "cp39-cp39m-macosx_10_14_x86_64.whl"
os: osx
osx_image: xcode11 # macOS 10.14
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.9
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.9/bin/python3'; alias pip='/usr/local/opt/python@3.9/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.14, Python 3.8
- name: "cp38-cp38m-macosx_10_14_x86_64.whl"
os: osx
osx_image: xcode11 # macOS 10.14
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.8
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.8/bin/python3'; alias pip='/usr/local/opt/python@3.8/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.14, Python 3.7
- name: "cp37-cp37m-macosx_10_14_x86_64.whl"
os: osx
osx_image: xcode11 # macOS 10.14
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.7
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.7/bin/python3'; alias pip='/usr/local/opt/python@3.7/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.13
# MacOS 10.13, Python 3.9
- name: "cp39-cp39m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode10.1 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.9
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.9/bin/python3'; alias pip='/usr/local/opt/python@3.9/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.13, Python 3.8
- name: "cp38-cp38m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode10.1 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.8
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.8/bin/python3'; alias pip='/usr/local/opt/python@3.8/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.13, Python 3.7
- name: "cp37-cp37m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode10.1 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python@3.7
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='/usr/local/opt/python@3.7/bin/python3'; alias pip='/usr/local/opt/python@3.7/bin/pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# MacOS 10.12, Python 3.7
# - name: "klayout python3 osx10.12"
- name: "cp37-cp37m-macosx_10_12_x86_64.whl"
os: osx
osx_image: xcode9.2 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- ccache
update: true
env:
- MATRIX_EVAL="shopt -s expand_aliases; alias python='python3'; alias pip='pip3';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
exclude:
# No XCode 8.2.1 for Mac OS 10.11
# - name: "klayout python3 osx10.11"
- name: "cp37-cp37m-macosx_10_11_x86_64.whl"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL="brew update; brew config; brew upgrade python; brew postinstall python; ls -l /usr/local/opt/python/libexec/bin/; shopt -s expand_aliases; alias python='/usr/local/opt/python/libexec/bin/python'; alias pip='/usr/local/opt/python/libexec/bin/pip';"
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# Python 2 is EOL
# python 2 osx
# - name: "klayout python2 osx10.13"
- name: "cp27-cp27m-macosx_10_13_x86_64.whl"
os: osx
osx_image: xcode9.4 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL=""
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# - name: "klayout python2 osx10.12"
- name: "cp27-cp27m-macosx_10_12_x86_64.whl"
os: osx
osx_image: xcode8.3 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL="brew install python2 || brew link --overwrite python@2" # deficient python2 in travis's xcode8.3 (no ssl)
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# - name: "klayout python2 osx10.11"
- name: "cp27-cp27m-macosx_10_11_x86_64.whl"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL=""
- ARCHFLAGS="-std=c++11"
- PIP_UPDATE="1"
- PYTHON_BUILD=true
# KLayout builds not enabled for now on MacOS
# KLayout builds for mac
# Python 3
- name: "KLayout macOS 10.13 with py3.7"
os: osx
osx_image: xcode9.4 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=B37
- MACOS_VERSION=HighSierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.12 with py3.7"
os: osx
osx_image: xcode8.3 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=B37
- MACOS_VERSION=Sierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.11 with py3.7"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- python3
- ccache
update: true
env:
- MATRIX_EVAL="brew update; brew config; brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/56c500b569c724b049be7ab9e12d9693f85522f9/Formula/qt.rb" # Qt 5.11.2
- PYTHON_VERSION=B37
- MACOS_VERSION=ElCapitan
- KLAYOUT_BUILD=true
# Python 2
- name: "KLayout macOS 10.13 with py2.7"
os: osx
osx_image: xcode9.4 # macOS 10.13
cache:
directories:
- ccache
addons:
homebrew:
packages:
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=Sys
- MACOS_VERSION=HighSierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.12 with py2.7"
os: osx
osx_image: xcode8.3 # macOS 10.12
cache:
directories:
- ccache
addons:
homebrew:
packages:
- qt
- ccache
update: true
env:
- MATRIX_EVAL=""
- PYTHON_VERSION=Sys
- MACOS_VERSION=Sierra
- KLAYOUT_BUILD=true
- name: "KLayout macOS 10.11 with py2.7"
os: osx
osx_image: xcode8 # macOS 10.11
cache:
directories:
- ccache
addons:
homebrew:
packages:
- ccache
update: true
env:
- MATRIX_EVAL="brew update; brew config; brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/56c500b569c724b049be7ab9e12d9693f85522f9/Formula/qt.rb" # Qt 5.11.2
- PYTHON_VERSION=Sys
- MACOS_VERSION=ElCapitan
- KLAYOUT_BUILD=true
before_install:
- env
- gem install dropbox-deployment
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
export PATH="/usr/local/opt/ccache/libexec:$PATH";
mkdir -p ccache;
export CCACHE_DIR="`pwd`/ccache";
ccache -s;
fi
- eval "${MATRIX_EVAL}"
- if [ "${PIP_UPDATE}" == "1" ]; then
pip --version;
pip install --upgrade pip || sudo pip install --upgrade pip;
pip --version;
pip install --upgrade setuptools wheel || sudo pip install --upgrade setuptools wheel;
fi
- python -c "import distutils.sysconfig as sysconfig; print(sysconfig.__file__)"
install:
- if [ "$DOCKER_BUILD" = true ]; then
docker pull $DOCKER_IMAGE;
fi
script:
- if [ "$DOCKER_BUILD" = true ]; then
mkdir -p ccache;
mkdir -p wheelhouse;
docker run --rm -e DOCKER_IMAGE -e PY_VERSION -v `pwd`:/io $DOCKER_IMAGE $PRE_CMD "/io/ci-scripts/docker/docker_build.sh";
klayout_version=$(python -c 'import setup; print(setup.Config().version())');
mkdir -p deploy/dist-pymod/$klayout_version;
cp -a wheelhouse/{klayout-*manylinux2014*.whl,*.zip} deploy/dist-pymod/$klayout_version;
if [ "$TEST_IN_HOST" = true ]; then
pip install klayout --no-index -f ./wheelhouse;
python testdata/pymod/import_db.py;
python testdata/pymod/import_rdb.py;
python testdata/pymod/import_tl.py;
python testdata/pymod/import_lib.py;
python testdata/pymod/pya_tests.py;
fi
fi
- if [ "$PYTHON_BUILD" = true ]; then
python setup.py build;
python setup.py bdist_wheel;
python setup.py install;
python setup.py sdist --formats=zip;
python testdata/pymod/import_db.py;
python testdata/pymod/import_rdb.py;
python testdata/pymod/import_tl.py;
python testdata/pymod/import_lib.py;
python testdata/pymod/pya_tests.py;
klayout_version=$(python -c 'import setup; print(setup.Config().version())');
mkdir -p deploy/dist-pymod/$klayout_version;
cp -a dist/{*.whl,*.zip} deploy/dist-pymod/$klayout_version;
python -c 'import klayout.db as db; print(dir(db))';
python -c 'import klayout.rdb as rdb; print(dir(rdb))';
python -c 'import klayout.tl as tl; print(dir(tl))';
fi
- if [ "$KLAYOUT_BUILD" = true ]; then
./travis-build.sh;
fi
before_cache:
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
ccache -s;
fi
after_success:
# upload to dropbox
# need DROPBOX_OAUTH_BEARER environment variable
- dropbox-deployment
# uploading to pypi using twine
# need TWINE_USERNAME, TWINE_PASSWORD and TWINE_REPOSITORY_URL env variable
- if [ ! -z "$TRAVIS_TAG" ] && [ ! -z "$TWINE_PASSWORD" ] && [[ "$PYTHON_BUILD" = true || "$DOCKER_BUILD" = true ]]; then
pip install -U twine || sudo pip install -U twine;
twine upload --skip-existing deploy/dist-pymod/$klayout_version/*;
fi

View File

@ -1,4 +0,0 @@
deploy:
dropbox_path: /Builds/klayout # The path to the folder on Dropbox where the files will go
artifacts_path: deploy # can be a single file, or a path
debug: true # if you want to see more logs

View File

@ -41,6 +41,9 @@ ubuntu22)
ubuntu24)
depends="libqt5core5t64 (>= 5.15.13), libqt5designer5 (>= 5.15.13), libqt5gui5t64 (>= 5.15.13), libqt5multimedia5 (>= 5.15.13), libqt5multimediawidgets5 (>= 5.15.13), libqt5network5t64 (>= 5.15.13), libqt5opengl5t64 (>= 5.15.13), libqt5printsupport5t64 (>= 5.15.13), libqt5sql5t64 (>= 5.15.13), libqt5svg5 (>= 5.15.13), libqt5widgets5t64 (>= 5.15.13), libqt5xml5t64 (>= 5.15.13), libqt5xmlpatterns5 (>= 5.15.13), zlib1g (>= 1.3), libgit2-1.7 (>= 1.7.2), libruby3.2 (>= 3.2.3), python3 (>= 3.12.3), libpython3.12 (>= 3.12.3), libstdc++6 (>=14), libc6 (>= 2.39)"
;;
ubuntu26)
depends="libqt5core5t64 (>= 5.15.18), libqt5designer5 (>= 5.15.18), libqt5gui5t64 (>= 5.15.18), libqt5multimedia5 (>= 5.15.18), libqt5multimediawidgets5 (>= 5.15.18), libqt5network5t64 (>= 5.15.18), libqt5opengl5t64 (>= 5.15.18), libqt5printsupport5t64 (>= 5.15.18), libqt5sql5t64 (>= 5.15.18), libqt5svg5 (>= 5.15.18), libqt5widgets5t64 (>= 5.15.18), libqt5xml5t64 (>= 5.15.18), libqt5xmlpatterns5 (>= 5.15.18), zlib1g (>= 1.3), libgit2-1.9 (>= 1.9.1), libruby3.3 (>= 3.3.8), python3 (>= 3.14.3), libpython3.14 (>= 3.14.3), libstdc++6 (>=16), libc6 (>= 2.43)"
;;
*)
echo "Unknown target '$target' (given as first argument)"
exit 1

View File

@ -1682,13 +1682,9 @@ CompoundRegionCheckOperationNode::computed_dist () const
void
CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout *layout, db::Cell *cell, const shape_interactions<db::PolygonWithProperties, db::PolygonWithProperties> &interactions, std::vector<std::unordered_set<db::EdgePairWithProperties> > &results, const db::LocalProcessorBase *proc) const
{
// consider magnification variants
db::EdgeRelationFilter check = m_check;
check.set_distance (proc->dist_for_cell (cell, check.distance ()));
// TODO: needs a concept to deal with merged/non-merged inputs
bool is_merged = true;
db::check_local_operation<db::PolygonWithProperties, db::PolygonWithProperties> op (check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
db::check_local_operation<db::PolygonWithProperties, db::PolygonWithProperties> op (m_check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
tl_assert (results.size () == 1);
if (results.front ().empty ()) {
@ -1704,13 +1700,9 @@ CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache
void
CompoundRegionCheckOperationNode::do_compute_local (CompoundRegionOperationCache * /*cache*/, db::Layout *layout, db::Cell *cell, const shape_interactions<db::PolygonRefWithProperties, db::PolygonRefWithProperties> &interactions, std::vector<std::unordered_set<db::EdgePairWithProperties> > &results, const db::LocalProcessorBase *proc) const
{
// consider magnification variants
db::EdgeRelationFilter check = m_check;
check.set_distance (proc->dist_for_cell (cell, check.distance ()));
// TODO: needs a concept to deal with merged/non-merged inputs
bool is_merged = true;
db::check_local_operation<db::PolygonRefWithProperties, db::PolygonRefWithProperties> op (check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
db::check_local_operation<db::PolygonRefWithProperties, db::PolygonRefWithProperties> op (m_check, m_different_polygons, is_merged, m_has_other, m_is_other_merged, m_options);
tl_assert (results.size () == 1);
if (results.front ().empty ()) {

View File

@ -2113,3 +2113,13 @@ TEST(148_sparse_array_limit)
run_test (_this, "148", true);
}
// issue 2384
TEST(149_compound_drc_with_mag)
{
run_test (_this, "149", false);
}
TEST(149d_compound_drc_with_mag)
{
run_test (_this, "149", true);
}

View File

@ -126,6 +126,40 @@ static std::vector<char> pixel_buffer_to_png (const tl::PixelBuffer *pb)
#endif
}
// Returns an 8-byte header (width, height as uint32) followed by the raw ARGB32 pixel data
static std::vector<char> pixel_buffer_to_bytes (const tl::PixelBuffer *pb)
{
uint32_t w = pb->width (), h = pb->height ();
size_t n = (size_t) w * (size_t) h * 4;
std::vector<char> data (8 + n);
memcpy (data.data (), &w, 4);
memcpy (data.data () + 4, &h, 4);
memcpy (data.data () + 8, pb->data (), n);
return data;
}
// Inverse of to_bytes: reads width/height from the header and checks the length
static tl::PixelBuffer pixel_buffer_from_bytes (const std::vector<char> &data)
{
uint32_t w = 0, h = 0;
if (data.size () >= 8) {
memcpy (&w, data.data (), 4);
memcpy (&h, data.data () + 4, 4);
}
// sanity guard against malformed headers: reader caps dimensions at 64k x 64k
// (generous for finely-resolved drawings), writer stays full 32-bit
const uint32_t max_dim = 65536;
if (w > max_dim || h > max_dim) {
throw tl::Exception (tl::to_string (tr ("Invalid PixelBuffer byte stream: width or height exceeds the 65536 x 65536 limit")));
}
if (data.size () != 8 + (size_t) w * (size_t) h * 4) {
throw tl::Exception (tl::to_string (tr ("Invalid PixelBuffer byte stream: length does not match the width and height in the header")));
}
tl::PixelBuffer pb (w, h, (const tl::color_t *) (data.data () + 8));
pb.set_transparent (true);
return pb;
}
Class<tl::PixelBuffer> decl_PixelBuffer ("lay", "PixelBuffer",
gsi::constructor ("new", &create_pixel_buffer, gsi::arg ("width"), gsi::arg ("height"),
@ -188,6 +222,24 @@ Class<tl::PixelBuffer> decl_PixelBuffer ("lay", "PixelBuffer",
"\n"
"This method may not be available if PNG support is not compiled into KLayout."
) +
gsi::method_ext ("to_bytes", &pixel_buffer_to_bytes,
"@brief Converts the pixel buffer to a raw byte stream\n"
"\n"
"The stream starts with an 8-byte header (width and height as 32-bit unsigned integers) "
"followed by the raw ARGB32 pixel data with 4 bytes per pixel in row-major order, "
"top to bottom. Unlike \\to_png_data this method has zero encoding overhead. "
"Use \\from_bytes to reconstruct the pixel buffer.\n"
"\n"
"This method has been added in version 0.30.10."
) +
gsi::method ("from_bytes", &pixel_buffer_from_bytes, gsi::arg ("data"),
"@brief Reconstructs a pixel buffer from a byte stream produced by \\to_bytes\n"
"\n"
"The width and height are taken from the header and the stream length is checked against them. "
"The dimensions are capped at 65536 x 65536; a header exceeding this limit raises an error.\n"
"\n"
"This method has been added in version 0.30.10."
) +
gsi::method ("patch", &tl::PixelBuffer::patch, gsi::arg ("other"),
"@brief Patches another pixel buffer into this one\n"
"\n"

View File

@ -855,7 +855,9 @@ LayoutCanvas::image_with_options (unsigned int width, unsigned int height, int l
lay::RedrawThread redraw_thread (&rd_canvas, mp_view);
// render the layout
redraw_thread.start (0 /*synchronous*/, m_layers, vp, resolution, font_resolution, true);
int workers = mp_view->synchronous () ? 0 : mp_view->drawing_workers ();
redraw_thread.start (workers, m_layers, vp, resolution, font_resolution, true);
redraw_thread.wait ();
redraw_thread.stop (); // safety
// paint the background objects. It uses "img" to paint on.

18
testdata/drc/drcSimpleTests_149.drc vendored Normal file
View File

@ -0,0 +1,18 @@
source $drc_test_source
target $drc_test_target
if $drc_test_deep
deep
end
deep
metal2_drawn = polygons(36, 0)
metal2_slot = polygons(36, 3)
metal2_drawn.output(36, 0)
metal2_slot.output(36, 3)
violations_enclosed = metal2_slot.drc(enclosed(metal2_drawn) < 10.um)
violations_enclosed.output(100, 0)

BIN
testdata/drc/drcSimpleTests_149.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au149.gds vendored Normal file

Binary file not shown.

BIN
testdata/drc/drcSimpleTests_au149d.gds vendored Normal file

Binary file not shown.

View File

@ -53,13 +53,18 @@ class DBShapesTest(unittest.TestCase):
# Issue #2012 (reference count)
def test_2(self):
# an empty dict provides the normal reference count
obj = {}
rc = sys.getrefcount(obj)
ly = pya.Layout()
self.assertEqual(sys.getrefcount(ly), rc)
top = ly.create_cell("TOP")
l1 = ly.layer(1, 0)
top.shapes(l1).insert(pya.Box(0, 0, 100, 200))
shapes = top.shapes(l1)
self.assertEqual(sys.getrefcount(shapes), 2)
self.assertEqual(sys.getrefcount(shapes), rc)
# Tests the ability to take PolygonWithProperties instead of base class
# for setter

View File

@ -57,6 +57,31 @@ class LAYPixelBufferTests(unittest.TestCase):
pb_copy = pya.PixelBuffer.read_png(tmp)
self.assertEqual(compare(pb, pb_copy), True)
def test_2(self):
# to_bytes / from_bytes round-trip
import struct
pb = pya.PixelBuffer(10, 20)
pb.transparent = True
pb.fill(0xf0010203)
pb.set_pixel(1, 2, 0x80102030)
data = bytes(pb.to_bytes())
self.assertEqual(len(data), 8 + 10 * 20 * 4)
self.assertEqual(struct.unpack("=II", data[0:8]), (10, 20)) # width, height header
self.assertEqual(pb == pya.PixelBuffer.from_bytes(data), True)
# a mismatched stream is rejected
with self.assertRaises(Exception):
pya.PixelBuffer.from_bytes(data[:-4])
# a header beyond the 64k x 64k limit is rejected
with self.assertRaises(Exception):
pya.PixelBuffer.from_bytes(struct.pack("=II", 65537, 1))
# run unit tests
if __name__ == '__main__':

View File

@ -152,6 +152,41 @@ class LAYPixelBuffer_TestClass < TestBase
end
def test_5
# to_bytes / from_bytes round-trip
pb = RBA::PixelBuffer::new(10, 20)
pb.transparent = true
pb.fill(0xf0010203)
pb.set_pixel(1, 2, 0x80102030)
bytes = pb.to_bytes
assert_equal(bytes.size, 8 + 10 * 20 * 4)
assert_equal(bytes[0, 8].unpack("VV"), [ 10, 20 ]) # width, height header
assert_equal(pb == RBA::PixelBuffer.from_bytes(bytes), true)
# a mismatched stream is rejected
error = false
begin
RBA::PixelBuffer.from_bytes(bytes[0, bytes.size - 4])
rescue
error = true
end
assert_equal(error, true)
# a header beyond the 64k x 64k limit is rejected
error = false
begin
RBA::PixelBuffer.from_bytes([ 65537, 1 ].pack("VV"))
rescue
error = true
end
assert_equal(error, true)
end
def test_11
pb = RBA::BitmapBuffer::new

View File

@ -1,37 +0,0 @@
#!/bin/bash
set -e
export PING_SLEEP=30s
bash -c "while true; do find qt* | wc -l; sleep $PING_SLEEP; done" &
PING_LOOP_PID=$!
touch build.txt
# Configure ccache
mkdir -p ccache;
export CCACHE_DIR="`pwd`/ccache"
export QMAKE_CCACHE=1
# Show ccache stats
echo "Cache stats:"
ccache -s
echo "build"
make build >> build.txt 2>&1 || tail -500 build.txt
echo "deploy"
make deploy >> build.txt 2>&1 || tail -500 build.txt
echo "test"
make test >> build.txt 2>&1 || tail -500 build.txt
echo "dropbox-deploy"
make dropbox-deploy
# Show ccache stats
echo "Cache stats:"
ccache -s
echo "build finished"
kill $PING_LOOP_PID
exit 0