shinestacker 1.5.2__py3-none-any.whl → 1.5.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shinestacker might be problematic. Click here for more details.

shinestacker/_version.py CHANGED
@@ -1 +1 @@
1
- __version__ = '1.5.2'
1
+ __version__ = '1.5.3'
shinestacker/app/main.py CHANGED
@@ -20,7 +20,7 @@ from shinestacker.app.gui_utils import (
20
20
  disable_macos_special_menu_items, fill_app_menu, set_css_style)
21
21
  from shinestacker.app.help_menu import add_help_action
22
22
  from shinestacker.app.open_frames import open_frames
23
- from .args import add_project_arguments, add_retouch_arguments
23
+ from shinestacker.app.args_parser_opts import add_project_arguments, add_retouch_arguments
24
24
 
25
25
 
26
26
  class SelectionDialog(QDialog):
@@ -17,7 +17,7 @@ from shinestacker.gui.main_window import MainWindow
17
17
  from shinestacker.app.gui_utils import (
18
18
  disable_macos_special_menu_items, fill_app_menu, set_css_style)
19
19
  from shinestacker.app.help_menu import add_help_action
20
- from .args import add_project_arguments
20
+ from shinestacker.app.args_parser_opts import add_project_arguments
21
21
 
22
22
 
23
23
  class ProjectApp(MainWindow):
@@ -13,7 +13,7 @@ from shinestacker.app.gui_utils import (
13
13
  disable_macos_special_menu_items, fill_app_menu, set_css_style)
14
14
  from shinestacker.app.help_menu import add_help_action
15
15
  from shinestacker.app.open_frames import open_frames
16
- from .args import add_retouch_arguments
16
+ from shinestacker.app.args_parser_opts import add_retouch_arguments
17
17
 
18
18
 
19
19
  class RetouchApp(ImageEditorUI):
@@ -26,7 +26,7 @@ class _GuiConstants:
26
26
  'outer': (255, 0, 0, 200),
27
27
  'inner': (255, 0, 0, 150),
28
28
  'gradient_end': (255, 0, 0, 0),
29
- 'pen': (255, 0, 0, 200),
29
+ 'pen': (255, 255, 255, 200),
30
30
  'preview': (255, 180, 180),
31
31
  'cursor_inner': (255, 0, 0, 120),
32
32
  'preview_inner': (255, 255, 255, 150)
@@ -1,4 +1,5 @@
1
1
  # pylint: disable=C0114, C0115, C0116, R0904, R0915, E0611, R0902, R0911, R0914, E1003
2
+ import time
2
3
  from PySide6.QtCore import Qt, Signal, QEvent, QRectF
3
4
  from PySide6.QtGui import QCursor
4
5
  from PySide6.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QFrame
@@ -58,6 +59,7 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
58
59
  self.current_view.setFocusPolicy(Qt.NoFocus)
59
60
  self.master_view.setFocusPolicy(Qt.NoFocus)
60
61
  self.current_brush_cursor = None
62
+ self.last_color_update_time_current = 0
61
63
 
62
64
  def setup_layout(self):
63
65
  raise NotImplementedError("Subclasses must implement setup_layout")
@@ -311,9 +313,11 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
311
313
  radius = size / 2
312
314
  self.current_brush_cursor.setRect(
313
315
  scene_pos.x() - radius, scene_pos.y() - radius, size, size)
316
+ self.update_current_cursor_color()
314
317
  self.current_brush_cursor.show()
315
318
  self.brush_cursor.setRect(
316
319
  scene_pos.x() - radius, scene_pos.y() - radius, size, size)
320
+ self.update_master_cursor_color()
317
321
  self.brush_cursor.show()
318
322
  else:
319
323
  self.brush_cursor.hide()
@@ -321,6 +325,19 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
321
325
  self.master_view.setCursor(Qt.ArrowCursor)
322
326
  self.current_view.setCursor(Qt.ArrowCursor)
323
327
 
328
+ def update_current_cursor_color(self):
329
+ self.update_cursor_color_based_on_background(
330
+ self.current_brush_cursor, self.current_layer(),
331
+ self.get_visible_current_image_region, self.get_current_pixmap,
332
+ self.update_color_time_current)
333
+
334
+ def update_color_time_current(self):
335
+ current_time = time.time()
336
+ if current_time - self.last_color_update_time_current < 0.2:
337
+ return False
338
+ self.last_color_update_time_current = current_time
339
+ return True
340
+
324
341
  def handle_master_mouse_press(self, event):
325
342
  self.setFocus()
326
343
  self.mouse_press_event(event)
@@ -425,6 +442,7 @@ class DoubleViewBase(ViewStrategy, QWidget, ViewSignals):
425
442
  scene_pos.x() - radius, scene_pos.y() - radius,
426
443
  size, size)
427
444
  if self.brush_cursor.isVisible():
445
+ self.update_current_cursor_color()
428
446
  self.current_brush_cursor.show()
429
447
  else:
430
448
  self.current_brush_cursor.hide()
@@ -1,5 +1,6 @@
1
1
  # pylint: disable=C0114, C0115, C0116, E0611, R0904, R0903, R0902, E1101, R0914, R0913, R0917
2
2
  import math
3
+ import time
3
4
  from abc import abstractmethod
4
5
  import numpy as np
5
6
  from PySide6.QtCore import Qt, QPointF, QTime, QPoint, Signal, QRectF
@@ -116,6 +117,7 @@ class ViewStrategy(LayerCollectionHandler):
116
117
  self.last_brush_pos = None
117
118
  self.last_mouse_pos = None
118
119
  self.last_update_time = QTime.currentTime()
120
+ self.last_color_update_time = 0
119
121
 
120
122
  @abstractmethod
121
123
  def create_pixmaps(self):
@@ -236,6 +238,8 @@ class ViewStrategy(LayerCollectionHandler):
236
238
 
237
239
  def set_cursor_style(self, style):
238
240
  self.cursor_style = style
241
+ if style == 'preview':
242
+ self.show_brush_preview()
239
243
  self.update_brush_cursor()
240
244
 
241
245
  def get_cursor_style(self):
@@ -402,9 +406,12 @@ class ViewStrategy(LayerCollectionHandler):
402
406
  self.update_cursor_pen_width()
403
407
 
404
408
  def setup_simple_brush_style(self, center_x, center_y, radius):
409
+ if self.brush_cursor:
410
+ pen = self.brush_cursor.pen()
411
+ else:
412
+ pen = QPen(QColor(*gui_constants.BRUSH_COLORS['pen']), self.current_line_width())
405
413
  gradient = create_default_brush_gradient(center_x, center_y, radius, self.brush)
406
- self.brush_cursor.setPen(QPen(QColor(*gui_constants.BRUSH_COLORS['pen']),
407
- self.current_line_width()))
414
+ self.brush_cursor.setPen(pen)
408
415
  self.brush_cursor.setBrush(QBrush(gradient))
409
416
 
410
417
  def create_circle(self, scene, line_style=Qt.SolidLine):
@@ -469,10 +476,58 @@ class ViewStrategy(LayerCollectionHandler):
469
476
  self.brush_preview.update(scene_pos, int(size))
470
477
  else:
471
478
  self.hide_brush_preview()
472
- if self.cursor_style != 'outline':
473
- self.setup_simple_brush_style(scene_pos.x(), scene_pos.y(), radius)
479
+ self.update_master_cursor_color()
480
+ if self.cursor_style == 'brush':
481
+ self.setup_simple_brush_style(scene_pos.x(), scene_pos.y(), radius)
474
482
  self.show_brush_cursor()
475
483
 
484
+ def update_color_time(self):
485
+ current_time = time.time()
486
+ if current_time - self.last_color_update_time < 0.2:
487
+ return False
488
+ self.last_color_update_time = current_time
489
+ return True
490
+
491
+ def update_master_cursor_color(self):
492
+ self.update_cursor_color_based_on_background(
493
+ self.brush_cursor, self.master_layer(),
494
+ self.get_visible_image_region, self.get_master_pixmap,
495
+ self.update_color_time)
496
+
497
+ def update_cursor_color_based_on_background(
498
+ self, cursor, layer,
499
+ visible_region, get_pixmap, update_timer):
500
+ if not update_timer():
501
+ return
502
+ cursor_rect = cursor.rect()
503
+ image_region = visible_region()
504
+ if image_region and cursor_rect.intersects(image_region):
505
+ intersect_rect = cursor_rect.intersected(image_region)
506
+ top_left = get_pixmap().mapFromScene(intersect_rect.topLeft())
507
+ bottom_right = get_pixmap().mapFromScene(intersect_rect.bottomRight())
508
+ x1, y1 = max(0, int(top_left.x())), max(0, int(top_left.y()))
509
+ x2, y2 = min(layer.shape[1], int(bottom_right.x())), \
510
+ min(layer.shape[0], int(bottom_right.y()))
511
+ if x2 > x1 and y2 > y1:
512
+ region = layer[y1:y2, x1:x2]
513
+ if region.size > 10000:
514
+ step = int(math.sqrt(region.size / 100))
515
+ region = region[::step, ::step]
516
+ if region.ndim == 3:
517
+ luminosity = np.dot(region[..., :3], [0.299, 0.587, 0.114])
518
+ avg_luminosity = np.mean(luminosity)
519
+ else:
520
+ avg_luminosity = np.mean(region)
521
+ if region.dtype == np.uint16:
522
+ avg_luminosity /= 256.0
523
+ if avg_luminosity < 128:
524
+ new_color = QColor(255, 255, 255)
525
+ else:
526
+ new_color = QColor(0, 0, 0)
527
+ current_pen = cursor.pen()
528
+ current_pen.setColor(new_color)
529
+ cursor.setPen(current_pen)
530
+
476
531
  def position_on_image(self, pos):
477
532
  master_view = self.get_master_view()
478
533
  pixmap = self.get_master_pixmap()
@@ -485,11 +540,20 @@ class ViewStrategy(LayerCollectionHandler):
485
540
  return None
486
541
  master_view = self.get_master_view()
487
542
  master_pixmap = self.get_master_pixmap()
488
- pixmap = self.get_master_pixmap()
489
543
  view_rect = master_view.viewport().rect()
490
544
  scene_rect = master_view.mapToScene(view_rect).boundingRect()
491
545
  image_rect = master_pixmap.mapFromScene(scene_rect).boundingRect().toRect()
492
- return image_rect.intersected(pixmap.boundingRect().toRect())
546
+ return image_rect.intersected(master_pixmap.boundingRect().toRect())
547
+
548
+ def get_visible_current_image_region(self):
549
+ if self.empty():
550
+ return None
551
+ current_view = self.get_current_view()
552
+ current_pixmap = self.get_current_pixmap()
553
+ view_rect = current_view.viewport().rect()
554
+ scene_rect = current_view.mapToScene(view_rect).boundingRect()
555
+ image_rect = current_pixmap.mapFromScene(scene_rect).boundingRect().toRect()
556
+ return image_rect.intersected(current_pixmap.boundingRect().toRect())
493
557
 
494
558
  def get_visible_image_portion(self):
495
559
  if self.has_no_master_layer():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shinestacker
3
- Version: 1.5.2
3
+ Version: 1.5.3
4
4
  Summary: ShineStacker
5
5
  Author-email: Luca Lista <luka.lista@gmail.com>
6
6
  License-Expression: LGPL-3.0
@@ -1,5 +1,5 @@
1
1
  shinestacker/__init__.py,sha256=uq2fjAw2z_6TpH3mOcWFZ98GoEPRsNhTAK8N0MMm_e8,448
2
- shinestacker/_version.py,sha256=7jrl0OaiREGeLqi1Vco5z6ma0w6Mxq8kDBXuswywlc8,21
2
+ shinestacker/_version.py,sha256=zG2cd7WLo5fIizFwE9xNzjpfZPff6uzTZIgsxS6ri_g,21
3
3
  shinestacker/algorithms/__init__.py,sha256=1FwVJ3w9GGbFFkjYJRUedTvcdE4j0ieSgaH9RC9iCY4,877
4
4
  shinestacker/algorithms/align.py,sha256=mb44u-YxZI1TTSHz81nRpX_2c8awlOhnGrK0LyfTQeQ,33543
5
5
  shinestacker/algorithms/align_auto.py,sha256=pJetw6zZEWQLouzcelkI8gD4cPiOp887ePXzVbm0E6Q,3800
@@ -22,17 +22,17 @@ shinestacker/algorithms/vignetting.py,sha256=gJOv-FN3GnTgaVn70W_6d-qbw3WmqinDiO9
22
22
  shinestacker/algorithms/white_balance.py,sha256=PMKsBtxOSn5aRr_Gkx1StHS4eN6kBN2EhNnhg4UG24g,501
23
23
  shinestacker/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
24
  shinestacker/app/about_dialog.py,sha256=pkH7nnxUP8yc0D3vRGd1jRb5cwi1nDVbQRk_OC9yLk8,4144
25
- shinestacker/app/args.py,sha256=TlfMR5GBd6Zz9wZNrN7CGJ1_hm1FnLZn5EoP5mkRHrk,776
25
+ shinestacker/app/args_parser_opts.py,sha256=TlfMR5GBd6Zz9wZNrN7CGJ1_hm1FnLZn5EoP5mkRHrk,776
26
26
  shinestacker/app/gui_utils.py,sha256=fSpkwPXTON_l676UHdAnJNrGq7BPbSlPOiHpOF_LZaI,2519
27
27
  shinestacker/app/help_menu.py,sha256=g8lKG_xZmXtNQaC3SIRzyROKVWva_PLEgZsQWh6zUcQ,499
28
- shinestacker/app/main.py,sha256=dcitc5vwIIyIXeDfZwQnC7KHRCdd3FaVJWyaU8mK86c,10935
28
+ shinestacker/app/main.py,sha256=UkWz4jvaBPH4Fs-arHe8H0NqKVTA4B_1e9BRn9S5oFo,10963
29
29
  shinestacker/app/open_frames.py,sha256=bsu32iJSYJQLe_tQQbvAU5DuMDVX6MRuNdE7B5lojZc,1488
30
- shinestacker/app/project.py,sha256=X98pK_mMtE_NefTUZfebEaP1YCsVY97hcQD4bSxuNyY,2777
31
- shinestacker/app/retouch.py,sha256=wlk-tHaei5YAFinGZWyzBopUhUqyxMT6jSH-4DMEwo8,2659
30
+ shinestacker/app/project.py,sha256=_kopeyb8e5JAA3XjNXoCshlYBfT5Avw-kTqiJN4GVlo,2805
31
+ shinestacker/app/retouch.py,sha256=On2oV2QonfMziQAvRkO6qw-h6E6wWQE0svKzZQKHeyg,2687
32
32
  shinestacker/config/__init__.py,sha256=aXxi-LmAvXd0daIFrVnTHE5OCaYeK1uf1BKMr7oaXQs,197
33
33
  shinestacker/config/config.py,sha256=eBko2D3ADhLTIm9X6hB_a_WsIjwgfE-qmBVkhP1XSvc,1636
34
34
  shinestacker/config/constants.py,sha256=EEdr7pZg4JpbIjUWaP7kJQfTuBB85FN739myDNAfn8A,8301
35
- shinestacker/config/gui_constants.py,sha256=Aqan-AdUjtlARXpsefQvWW2uKVv1tvwc0gfKyo7xud4,2787
35
+ shinestacker/config/gui_constants.py,sha256=55Qr0KzrTx8eQHCkT-EVabSiD59VvLZIh5o2cA9028s,2791
36
36
  shinestacker/core/__init__.py,sha256=IUEIx6SQ3DygDEHN3_E6uKpHjHtUa4a_U_1dLd_8yEU,484
37
37
  shinestacker/core/colors.py,sha256=kr_tJA1iRsdck2JaYDb2lS-codZ4Ty9gdu3kHfiWvuM,1340
38
38
  shinestacker/core/core_utils.py,sha256=1LYj19Dfc9jZN9-4dlf1paximDH5WZYa7DXvKr7R7QY,1719
@@ -90,16 +90,16 @@ shinestacker/retouch/io_manager.py,sha256=JUAA--AK0mVa1PTErJTnBFjaXIle5Qs7Ow0Wkd
90
90
  shinestacker/retouch/layer_collection.py,sha256=fZlGrkm9-Ycc7AOzFSpImhafiTieBeCZRk-UlvlFHbo,5819
91
91
  shinestacker/retouch/overlaid_view.py,sha256=uIMolD1984uQPWXpd27tMvGBTcSrYT-4vxO0pWVlEO4,8686
92
92
  shinestacker/retouch/shortcuts_help.py,sha256=BFWTT5QvodqMhqa_9LI25hZqjICfckgyWG4fGrGzvnM,4283
93
- shinestacker/retouch/sidebyside_view.py,sha256=dN5uDG0ioFvYcbmZbx97slh4cOLmJ2T14jtTshg5F9w,17918
93
+ shinestacker/retouch/sidebyside_view.py,sha256=2BrIrr38FxYJ522GLpHomScwbJgAczO9LiPwTAIlPUE,18647
94
94
  shinestacker/retouch/transformation_manager.py,sha256=NSHGUF-JFv4Y81gSvizjQCTp49TLo1so7c0WoUElO08,1812
95
95
  shinestacker/retouch/undo_manager.py,sha256=cKUkqnJtnJ-Hq-LQs5Bv49FC6qkG6XSw9oCVySJ8jS0,4312
96
96
  shinestacker/retouch/unsharp_mask_filter.py,sha256=Iapc8UmSVpj3V0LcJq_38P5qerRqTevMynbbk5Rk6iE,3634
97
- shinestacker/retouch/view_strategy.py,sha256=_Zo-SU2hlVVSv1g5eWgp72njcmfMpAlkkPggd89XJFo,23326
97
+ shinestacker/retouch/view_strategy.py,sha256=Di0iaYwTMI58pheF51d9MBJ0eLDvJog1DnmBLNMMMRs,26100
98
98
  shinestacker/retouch/vignetting_filter.py,sha256=JhFr6OVIripQzSJrZEG4lxq7wBsmpofLqJQ-aP2bKw8,3789
99
99
  shinestacker/retouch/white_balance_filter.py,sha256=QlMnzWmBYqUQqckY8XTH3dWPdqo7mz4gTwTZafxldPw,8237
100
- shinestacker-1.5.2.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
101
- shinestacker-1.5.2.dist-info/METADATA,sha256=AuETmZdF7h5LSior9SUBcdWaiSmCxenl_9bECasvSLk,6978
102
- shinestacker-1.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
103
- shinestacker-1.5.2.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
104
- shinestacker-1.5.2.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
105
- shinestacker-1.5.2.dist-info/RECORD,,
100
+ shinestacker-1.5.3.dist-info/licenses/LICENSE,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
101
+ shinestacker-1.5.3.dist-info/METADATA,sha256=8-9HAKfGrk6TEYj1rr_O6habe-0YlUGpHrrlhmQvRwY,6978
102
+ shinestacker-1.5.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
103
+ shinestacker-1.5.3.dist-info/entry_points.txt,sha256=SY6g1LqtMmp23q1DGwLUDT_dhLX9iss8DvWkiWLyo_4,166
104
+ shinestacker-1.5.3.dist-info/top_level.txt,sha256=MhijwnBVX5psfsyX8JZjqp3SYiWPsKe69f3Gnyze4Fw,13
105
+ shinestacker-1.5.3.dist-info/RECORD,,
File without changes