diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5d283f51..343843f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -56,9 +56,7 @@ jobs: - name: Setup run: | - #choco --yes install winfsp - py -m pip install -U pytest - py -m pip install -U -r requirements.txt + python -m pip install -U -r requirements.txt - name: Test - run: cd autoortho && py -m pytest -v test_getortho.py test_pydds.py test_downloader.py + run: cd autoortho && python -m pytest -v test_getortho.py test_pydds.py test_downloader.py diff --git a/autoortho/aoconfig.py b/autoortho/aoconfig.py index 76c5f8ff..03e8de20 100644 --- a/autoortho/aoconfig.py +++ b/autoortho/aoconfig.py @@ -101,6 +101,9 @@ class AOConfig(object): [windows] prefer_winfsp = False + +[coloring] +saturation = 100 """ def __init__(self, conf_file=None): @@ -151,6 +154,7 @@ def get_config(self): sceneries = [] if os.path.exists(self.ao_scenery_path): sceneries = os.listdir(self.ao_scenery_path) + sceneries = [s for s in sceneries if s != '.DS_Store'] print(f"Found sceneries: {sceneries}") self.scenery_mounts = [{ diff --git a/autoortho/aoimage/AoImage.py b/autoortho/aoimage/AoImage.py index 9735a101..21ea13c6 100755 --- a/autoortho/aoimage/AoImage.py +++ b/autoortho/aoimage/AoImage.py @@ -105,10 +105,27 @@ def paste(self, p_img, pos): _aoi.aoimage_paste(self, p_img, pos[0], pos[1]) return True + def copy(self, height_only = 0): + new = AoImage() + if not _aoi.aoimage_copy(self, new, height_only): + log.error(f"AoImage.copy error: {self._errmsg.decode()}") + return None + + return new + def crop(self, c_img, pos): _aoi.aoimage_crop(self, c_img, pos[0], pos[1]) return True + def desaturate(self, saturation = 1.0): + assert 0.0 <= saturation and saturation <= 1.0 + if saturation == 1.0 or saturation is None: + return self + + if not _aoi.aoimage_desaturate(self, saturation): + log.error(f"AoImage.desaturate error: {self._errmsg.decode()}") + return None + return self @property def size(self): @@ -165,8 +182,10 @@ def open(filename): _aoi.aoimage_create.argtypes = (POINTER(AoImage), c_uint32, c_uint32, c_uint32, c_uint32, c_uint32) _aoi.aoimage_tobytes.argtypes = (POINTER(AoImage), c_char_p) _aoi.aoimage_from_memory.argtypes = (POINTER(AoImage), c_char_p, c_uint32) +_aoi.aoimage_copy.argtypes = (POINTER(AoImage), POINTER(AoImage), c_uint32) _aoi.aoimage_paste.argtypes = (POINTER(AoImage), POINTER(AoImage), c_uint32, c_uint32) _aoi.aoimage_crop.argtypes = (POINTER(AoImage), POINTER(AoImage), c_uint32, c_uint32) +_aoi.aoimage_desaturate.argtypes = (POINTER(AoImage), c_float) def main(): logging.basicConfig(level = logging.DEBUG) @@ -194,6 +213,8 @@ def main(): img = open("../testfiles/test_tile2.jpg") log.info(f"AoImage.open {img}") + img.copy().desaturate(0.1).write_jpg("desaturated.jpg") + img2 = img.reduce_2() log.info(f"img2: {img2}") diff --git a/autoortho/aoimage/Makefile.osx b/autoortho/aoimage/Makefile.osx index ba70e0e1..ea82a11e 100644 --- a/autoortho/aoimage/Makefile.osx +++ b/autoortho/aoimage/Makefile.osx @@ -1,42 +1,40 @@ .SUFFIXES: .obj TARGET=main -JPGT=/opt/libjpeg-turbo/lib64/ -#JPGT=/opt/homebrew/opt/jpeg-turbo/lib + +# When jpeg-turbo comes from homebrew, it will only be available for the architecture that homebrew was setup for. +# We can have two parallel installation of homebrew though. The arch64 goes by default goes into /opt/homebrew +# and x86_64 goes into /usr/local. From there, we can pull the static libs and mesh them into a univesal binary +# There are some warnings about missing platform flags at link time but it seems like they can be ignored + +JPGT_ARM64=/opt/homebrew/Cellar/jpeg-turbo/3.0.2 +JPGT_X86_64=/usr/local/Cellar/jpeg-turbo/3.0.2 HEADERS=$(wildcard *.h) OBJECTS=aoimage.o - CC=gcc LD=gcc -CFLAGS+=-O2 -arch arm64 -arch x86_64 -Wall -fPIC -fdiagnostics-color -fvisibility=hidden -I/opt/libjpeg-turbo/include/ \ +CFLAGS+=-O2 -arch arm64 -arch x86_64 -Wall -fPIC -fdiagnostics-color -fvisibility=hidden -I$(JPGT_ARM64)/include/ \ $(DEFINES) LDFLAGS=-shared -rdynamic -nodefaultlibs -arch arm64 -arch x86_64 -lpthread -#LIBS=-lturbojpeg -# Statically linked with libjpeg-turbo-3.0.1 -LIBS=$(JPGT)/libturbojpeg.a -#LIBS=-L$(JPGT) -lturbojpeg - -#CFLAGS+=-O2 -Wall -fPIC -fdiagnostics-color -fvisibility=hidden \ - $(DEFINES) -# -#LDFLAGS=-shared -rdynamic -nodefaultlibs -lpthread -#LIBS=-L$(JPGT) -lturbojpeg all: $(TARGET) .c.o: $(HEADERS) $(CC) $(CFLAGS) -c $< -main: main.c aoimage.dylib $(HEADERS) +main: main.c aoimage.dylib lib/libturbojpeg.a $(HEADERS) $(CC) $(CFLAGS) -o main \ - main.c aoimage.c $(LIBS) + main.c aoimage.c lib/libturbojpeg.a + +lib/libturbojpeg.a: + lipo -create -output lib/libturbojpeg.a $(JPGT_X86_64)/lib/libturbojpeg.a $(JPGT_ARM64)/lib/libturbojpeg.a -aoimage.dylib: $(OBJECTS) - $(LD) -o aoimage.dylib $(LDFLAGS) $(OBJECTS) $(LIBS) +aoimage.dylib: $(OBJECTS) lib/libturbojpeg.a + $(LD) -o aoimage.dylib $(LDFLAGS) $(OBJECTS) lib/libturbojpeg.a clean: - rm -f $(OBJECTS) $(TARGET) aoimage.dylib + rm -f $(OBJECTS) $(TARGET) lib/libturbojpeg.a aoimage.dylib diff --git a/autoortho/aoimage/aoimage.c b/autoortho/aoimage/aoimage.c index 7744e65b..e5505d18 100755 --- a/autoortho/aoimage/aoimage.c +++ b/autoortho/aoimage/aoimage.c @@ -397,6 +397,32 @@ AOIAPI void aoimage_tobytes(aoimage_t *img, uint8_t *data) { memcpy(data, img->ptr, img->width * img->height * img->channels); } +AOIAPI int32_t aoimage_copy(const aoimage_t *s_img, aoimage_t *d_img, uint32_t s_height_only) { + assert(NULL != s_img->ptr); + assert(s_height_only <= s_img->height); + + if (0 == s_height_only) + s_height_only = s_img->height; + + int dlen = s_img->width * s_height_only * s_img->channels; + uint8_t *dest = malloc(dlen); + if (NULL == dest) { + sprintf(d_img->errmsg, "can't malloc %d bytes", dlen); + d_img->ptr = NULL; + return FALSE; + } + + memcpy(dest, s_img->ptr, dlen); + d_img->ptr = dest; + d_img->width = s_img->width; + d_img->height = s_height_only; + d_img->stride = 4 * d_img->width; + d_img->channels = 4; + d_img->errmsg[0] = '\0'; + return TRUE; + +} + AOIAPI int32_t aoimage_paste(aoimage_t *img, const aoimage_t *p_img, uint32_t x, uint32_t y) { assert(x + p_img->width <= img->width); assert(y + p_img->height <= img->height); @@ -435,4 +461,18 @@ AOIAPI int32_t aoimage_crop(aoimage_t *img, const aoimage_t *c_img, uint32_t x, return TRUE; } +AOIAPI int32_t aoimage_desaturate(aoimage_t *img, float saturation) { + assert(img->channels == 4); + + int len = img->width * img->height * 4; + for (uint8_t *ptr = img->ptr; ptr < img->ptr + len; ptr += 4) { + float luma = 0.212671f * ptr[0] + 0.715160f * ptr[1] + 0.072169f * ptr[2]; + float x = (1.0f - saturation) * luma; + ptr[0] = (uint8_t)(saturation * ptr[0] + x); + ptr[1] = (uint8_t)(saturation * ptr[1] + x); + ptr[2] = (uint8_t)(saturation * ptr[2] + x); + } + + return TRUE; +} diff --git a/autoortho/aoimage/aoimage.dll b/autoortho/aoimage/aoimage.dll index 18f07b51..fabd58e5 100755 Binary files a/autoortho/aoimage/aoimage.dll and b/autoortho/aoimage/aoimage.dll differ diff --git a/autoortho/aoimage/aoimage.dylib b/autoortho/aoimage/aoimage.dylib index 8284e1f5..5c32f113 100755 Binary files a/autoortho/aoimage/aoimage.dylib and b/autoortho/aoimage/aoimage.dylib differ diff --git a/autoortho/aoimage/aoimage.h b/autoortho/aoimage/aoimage.h index 64f8a763..1c4347ca 100755 --- a/autoortho/aoimage/aoimage.h +++ b/autoortho/aoimage/aoimage.h @@ -47,8 +47,12 @@ AOIAPI void aoimage_delete(aoimage_t *img); AOIAPI int32_t aoimage_create(aoimage_t *img, uint32_t width, uint32_t height, uint32_t r, uint32_t g, uint32_t b); AOIAPI int32_t aoimage_from_memory(aoimage_t *img, const uint8_t *data, uint32_t len); AOIAPI void aoimage_tobytes(aoimage_t *img, uint8_t *data); +AOIAPI int32_t aoimage_copy(const aoimage_t *s_img, aoimage_t *d_img, uint32_t s_height_only); // in place: img + pasted(p_img) AOIAPI int32_t aoimage_paste(aoimage_t *img, const aoimage_t *p_img, uint32_t x, uint32_t y); AOIAPI int32_t aoimage_crop(aoimage_t *img, const aoimage_t *c_img, uint32_t x, uint32_t y); + +// in place desaturation +AOIAPI int32_t aoimage_desaturate(aoimage_t *img, float saturation); #endif diff --git a/autoortho/aoimage/aoimage.so b/autoortho/aoimage/aoimage.so index 2d613ade..c08dfdda 100755 Binary files a/autoortho/aoimage/aoimage.so and b/autoortho/aoimage/aoimage.so differ diff --git a/autoortho/aoimage/lib/libturbojpeg.a b/autoortho/aoimage/lib/libturbojpeg.a index 92841f88..f564a4e8 100644 Binary files a/autoortho/aoimage/lib/libturbojpeg.a and b/autoortho/aoimage/lib/libturbojpeg.a differ diff --git a/autoortho/autoortho_fuse.py b/autoortho/autoortho_fuse.py index 10278b62..8ba1cd2b 100644 --- a/autoortho/autoortho_fuse.py +++ b/autoortho/autoortho_fuse.py @@ -70,6 +70,8 @@ class AutoOrtho(Operations): path_dict = {} tile_dict = {} + fh_locks = {} + fh = 1000 default_uid = -1 @@ -390,9 +392,10 @@ def read(self, path, length, offset, fh): return data if not data: - os.lseek(fh, offset, os.SEEK_SET) - data = os.read(fh, length) - log.debug(f"READ: Read {len(data)} bytes.") + with self.fh_locks.setdefault(fh, threading.Lock()): + os.lseek(fh, offset, os.SEEK_SET) + data = os.read(fh, length) + log.debug(f"READ: Read {len(data)} bytes.") return data @@ -515,7 +518,7 @@ def run(ao, mountpoint, nothreads=False): parser.add_argument('mount') args = parser.parse_args() - logging.basicConfig(level=logging.DEBUG) + logging.basicConfig(level=logging.INFO) ao = AutoOrtho(args.root) fuse = FUSE(ao, args.mount, foreground=True, allow_other=True) diff --git a/autoortho/config_ui.py b/autoortho/config_ui.py index 0029cade..3a72c40d 100644 --- a/autoortho/config_ui.py +++ b/autoortho/config_ui.py @@ -18,7 +18,7 @@ import logging log = logging.getLogger(__name__) -import PySimpleGUI as sg +import FreeSimpleGUI as sg import downloader from version import __version__ @@ -171,6 +171,17 @@ def ui_loop(self): # metadata={'section':self.cfg.cache} #), ], + [ + sg.Text('Saturation'), + sg.Slider( + range=(0, 100, 5), + default_value = self.cfg.coloring.saturation, + key='saturation', + size=(20,15), + orientation='horizontal', + metadata={'section':self.cfg.coloring} + ), + ], #[ # sg.Checkbox('Cleanup cache on start', key='clean_on_start', # default=self.cfg.cache.clean_on_start, diff --git a/autoortho/downloader.py b/autoortho/downloader.py index fbb6aa2f..a28b1a0f 100644 --- a/autoortho/downloader.py +++ b/autoortho/downloader.py @@ -224,7 +224,8 @@ def _show_progress(self, block_num, block_size, total_size): MBps = (total_fetched/1048576) / elapsed cur_activity['pcnt_done'] = pcnt_done cur_activity['MBps'] = MBps - print(f"\r{pcnt_done:.2f}% {MBps:.2f} MBps", end='') + if block_num % 1000 == 0: + print(f"\r{pcnt_done:.2f}% {MBps:.2f} MBps", end='') cur_activity['status'] = f"Downloading {self.dl_url}\n{pcnt_done:.2f}% {MBps:.2f} MBps" def check(self): diff --git a/autoortho/getortho.py b/autoortho/getortho.py index 76072b42..8232a40b 100644 --- a/autoortho/getortho.py +++ b/autoortho/getortho.py @@ -421,6 +421,8 @@ def __init__(self, col, row, maptype, zoom, min_zoom=0, priority=0, dxt_format=CFG.pydds.format) self.id = f"{row}_{col}_{maptype}_{zoom}" + # in % in the CFG + self.saturation = 0.01 * float(CFG.coloring.saturation) def __lt__(self, other): return self.priority < other.priority @@ -649,8 +651,18 @@ def get_header(self): self.ready.set() return outfile - @locked def get_img(self, mipmap, startrow=0, endrow=None, maxwait=5, min_zoom=None): + im = self._get_img(mipmap, startrow, endrow, maxwait, min_zoom) + if im is None: + return None + + if self.saturation < 1.0: + im = im.copy().desaturate(self.saturation) + + return im + + @locked + def _get_img(self, mipmap, startrow=0, endrow=None, maxwait=5, min_zoom=None): # # Get an image for a particular mipmap # diff --git a/autoortho/lib/darwin_universal/README.md b/autoortho/lib/darwin_universal/README.md new file mode 100644 index 00000000..bab8b29b --- /dev/null +++ b/autoortho/lib/darwin_universal/README.md @@ -0,0 +1,11 @@ +Building universal libstbd +git clone https://github.com/nothings/stb.git + +create stb_dxt.c with +#include "stb_dxt.h" +#define STB_DXT_IMPLEMENTATION + +build dynlib with +clang -arch x86_64 -arch arm64 -c stb_dxt.c +clang -o libstbdxt.dylib -shared -rdynamic -nodefaultlibs -arch arm64 -arch x86_64 stb_dxt.o + diff --git a/autoortho/lib/darwin_universal/libispc_texcomp.dylib b/autoortho/lib/darwin_universal/libispc_texcomp.dylib new file mode 100644 index 00000000..dd8ecfb4 Binary files /dev/null and b/autoortho/lib/darwin_universal/libispc_texcomp.dylib differ diff --git a/autoortho/lib/darwin_universal/libstbdxt.dylib b/autoortho/lib/darwin_universal/libstbdxt.dylib new file mode 100755 index 00000000..1da1a982 Binary files /dev/null and b/autoortho/lib/darwin_universal/libstbdxt.dylib differ diff --git a/docs/img.png b/docs/img.png new file mode 100644 index 00000000..5b07701d Binary files /dev/null and b/docs/img.png differ diff --git a/docs/index.md b/docs/index.md index 71dda87e..acfedf7b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -59,6 +59,82 @@ experimental ATM: 8. Configure your scenery_packs.ini file appropriately 9. Run X-Plane and choose a location for an ortho set you have downloaded + +## MacOS Setup (See NOTE below) + +>NOTE: I will no longer update this branch to match upstream changes. That means MacOS support will end beyond 0.7.2. Anyone could branch off here and pull upstream changes they want in the future. I have decided to branch off and add my own thing in the future as original author never check our PR. + +All of below steps require running command in ``Ternimal`` app. + +1. Install [Homebrew](https://brew.sh/) if you don't have it yet. +2. Install [FUSE for macOS]( ://osxfuse.github.io/) with command ``brew install --cask macfuse``. +> Note: You need to enable FUSE in ``System Preferences`` -> ``Security & Privacy`` -> ``General`` -> ``Allow apps downloaded from:`` -> ``App Store and identified developers``. Watch some youtobe videos if you don't know how to do it. + +3. Install [xcode command line tools](https://developer.apple.com/xcode/resources/) with command ``xcode-select --install``. +> Note: if you have already installed xcode, you can skip this step. but make sure it is up-to-date. +4. Clone the code +```shell +git clone https://github.com/zodiac1214/autoortho.git + +cd autoortho && git checkout macos-0.7.2 +``` +5. install python3.10 with command ``brew install python@3.10``. +6. install jpeg-turbo with command ``brew install jpeg-turbo``. +7. brew install tcl-tk with command ``brew install tcl-tk``. +8. Run command `brew tap --force homebrew/core`. +9. modify brew tcl-tk +``` +brew edit tcl-tk +``` +Us your arrow keys to go down to +``` +def install +args = %W[ +--prefix=#{prefix} +--includedir=#{include}/tcl-tk +--mandir=#{man} +--enable-threads +--enable-64bit +] +``` +Then press ā€œiā€ + +Then paste this line in that space + +`ENV["MACOSX_DEPLOYMENT_TARGET"] = "10.11"` + +Make sure it is aligned with the line after it. + +It should look like this after +![img.png](img.png) + +After this hit ESC on your keyboard then type the following: + +`:wq!` (including the colon) + +Then hit enter + +10. reinstall tcl-tk with modified brew tcl-tk +```shell +HOMEBREW_NO_INSTALL_FROM_API=1 brew reinstall --build-from-source tcl-tk +``` +11. install python-tk with command ``brew install python-tk@3.10``. +12. install python dependencies. +``` +python3.10 -m pip install -U pip +python3.10 -m pip install setuptools +python3.10 -m pip install wheel +python3.10 -m pip install -r requirements-build.txt --no-use-pep517 +python3.10 -m pip install -r requirements.txt +``` +13. Remove any MacOS quarantine flags +``` shell +sudo xattr -dr com.apple.quarantine * +``` +14. Run it +``` shell +python3.10 autoortho +``` --- ## Requirements and Compatibility diff --git a/requirements.in b/requirements.in index 602f6b2c..30dc0208 100644 --- a/requirements.in +++ b/requirements.in @@ -1,6 +1,6 @@ refuse psutil -pysimplegui +freesimplegui Flask flask-socketio eventlet diff --git a/requirements.txt b/requirements.txt index e689829f..4278534d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -54,7 +54,7 @@ pluggy==1.2.0 # via pytest psutil==5.9.2 # via -r requirements.in -pysimplegui==4.60.4 +freesimplegui>5 # via -r requirements.in pytest==7.4.0 # via -r requirements.in