-
Notifications
You must be signed in to change notification settings - Fork 1
Test #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Test #23
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -129,30 +129,40 @@ def load_image(self, filepath: str, cached_preview: Optional[DecodedImage] = Non | |
| self._preview_image = None | ||
| return False | ||
|
|
||
| def _apply_edits(self, img: Image.Image) -> Image.Image: | ||
| def _apply_edits(self, img: Image.Image, is_export: bool = False) -> Image.Image: | ||
| """Applies all current edits to the provided PIL Image.""" | ||
| # 1. Rotation | ||
| rotation = self.current_edits['rotation'] | ||
| if rotation == 90: | ||
| img = img.transpose(Image.Transpose.ROTATE_90) | ||
| img = img.transpose(Image.Transpose.ROTATE_270) # 90 CW = 270 CCW | ||
| elif rotation == 180: | ||
| img = img.transpose(Image.Transpose.ROTATE_180) | ||
| elif rotation == 270: | ||
| img = img.transpose(Image.Transpose.ROTATE_270) | ||
| img = img.transpose(Image.Transpose.ROTATE_90) # 270 CW = 90 CCW | ||
|
|
||
| # 2. Free Rotation (Straighten) | ||
| straighten_angle = self.current_edits['straighten_angle'] | ||
| if abs(straighten_angle) > 0.001: | ||
| img = img.rotate(straighten_angle, resample=Image.Resampling.BICUBIC, expand=True) | ||
| # PIL rotate is CCW. We want UI CW. Use negative. | ||
| # expand=True changes dimensions. | ||
| img = img.rotate(-straighten_angle, resample=Image.Resampling.BICUBIC, expand=True) | ||
|
|
||
| # 3. Cropping | ||
| crop_box = self.current_edits.get('crop_box') | ||
| if crop_box: | ||
| if crop_box and len(crop_box) == 4: | ||
| width, height = img.size | ||
| # Normalized 0-1000 to pixels | ||
| left = int(crop_box[0] * width / 1000) | ||
| top = int(crop_box[1] * height / 1000) | ||
| right = int(crop_box[2] * width / 1000) | ||
| bottom = int(crop_box[3] * height / 1000) | ||
|
|
||
| # Bounds check | ||
| left = max(0, min(width - 1, left)) | ||
| top = max(0, min(height - 1, top)) | ||
| right = max(left + 1, min(width, right)) | ||
| bottom = max(top + 1, min(height, bottom)) | ||
|
|
||
| img = img.crop((left, top, right, bottom)) | ||
|
Comment on lines
150
to
166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: find . -type f -name "*test*.py" | grep -i "editor\|imaging" | head -20Repository: AlanRockefeller/faststack Length of output: 111 🏁 Script executed: rg -l "crop" --type=py | grep -i testRepository: AlanRockefeller/faststack Length of output: 51 🏁 Script executed: rg -n "def test.*crop" --type=pyRepository: AlanRockefeller/faststack Length of output: 51 🏁 Script executed: # Also check the structure of imaging tests
fd -t f "test" faststack/faststack/imaging/ 2>/dev/null || echo "No imaging tests found in that exact location"Repository: AlanRockefeller/faststack Length of output: 51 🏁 Script executed: # Look for pytest test directory
fd -t d "test" faststack/ 2>/dev/null | head -10Repository: AlanRockefeller/faststack Length of output: 95 🏁 Script executed: cat -n faststack/faststack/tests/test_editor.pyRepository: AlanRockefeller/faststack Length of output: 1003 🏁 Script executed: # Also check the editor.py file to understand the full cropping implementation
wc -l faststack/faststack/imaging/editor.pyRepository: AlanRockefeller/faststack Length of output: 110 🏁 Script executed: # Get the cropping method context
sed -n '140,180p' faststack/faststack/imaging/editor.pyRepository: AlanRockefeller/faststack Length of output: 1850 🏁 Script executed: # Double-check if there are any other test files that might test crop functionality
rg -l "ImageEditor\|crop" faststack/faststack/tests/ --type=pyRepository: AlanRockefeller/faststack Length of output: 51 Add pytest test cases for the cropping logic in Per coding guidelines, every new branch in 🤖 Prompt for AI Agents |
||
|
|
||
| # 3. Exposure (gamma-based) | ||
|
|
@@ -350,7 +360,7 @@ def get_preview_data(self) -> Optional[DecodedImage]: | |
|
|
||
| # Always start from a fresh copy of the small preview image | ||
| img = self._preview_image.copy() | ||
| img = self._apply_edits(img) | ||
| img = self._apply_edits(img, is_export=False) | ||
|
|
||
| # The image is in RGB mode after _apply_edits | ||
| buffer = img.tobytes() | ||
|
|
@@ -383,7 +393,7 @@ def save_image(self) -> Optional[Tuple[Path, Path]]: | |
| return None | ||
|
|
||
| final_img = self.original_image.copy() | ||
| final_img = self._apply_edits(final_img) | ||
| final_img = self._apply_edits(final_img, is_export=True) | ||
|
|
||
| original_path = self.current_filepath | ||
| try: | ||
|
|
@@ -402,14 +412,38 @@ def save_image(self) -> Optional[Tuple[Path, Path]]: | |
| # Re-open original to correctly detect format and get EXIF | ||
| with Image.open(original_path) as original_img: | ||
| original_format = original_img.format or original_path.suffix.lstrip('.').upper() | ||
| exif_data = original_img.info.get('exif') | ||
|
|
||
| # Handle EXIF | ||
| exif_bytes = original_img.info.get('exif') | ||
|
|
||
| # Try to reset orientation to Normal (1) if EXIF exists | ||
| if exif_bytes: | ||
| try: | ||
| # Load exif data as an object | ||
| exif = original_img.getexif() | ||
| # Tag 274 is Orientation. Set to 1 (Normal) | ||
| if 274 in exif: | ||
| exif[274] = 1 | ||
| # Serialize back to bytes - Pillow >= 8.2.0 required for tobytes() | ||
| # If tobytes() is missing, we might skip writing modified EXIF or write original | ||
| if hasattr(exif, 'tobytes'): | ||
| exif_bytes = exif.tobytes() | ||
| else: | ||
| # Fallback for older Pillow: skip writing EXIF if we can't sanitize it | ||
| # to avoid double-rotation bug. | ||
| print("Warning: Pillow too old to sanitize EXIF bytes. Skipping EXIF write to prevent double-rotation.") | ||
| exif_bytes = None | ||
| except Exception as e: | ||
| print(f"Warning: Failed to sanitize EXIF orientation: {e}") | ||
| # Fallback: safer to skip EXIF than write bad orientation | ||
| exif_bytes = None | ||
|
|
||
| save_kwargs = {} | ||
| if original_format == 'JPEG': | ||
| save_kwargs['format'] = 'JPEG' | ||
| save_kwargs['quality'] = 95 | ||
| if exif_data: | ||
| save_kwargs['exif'] = exif_data | ||
| if exif_bytes: | ||
| save_kwargs['exif'] = exif_bytes | ||
| else: | ||
| save_kwargs['format'] = original_format | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove extraneous
+prefix from keyboard shortcut entries.The lines appear to have
+-as a prefix, which looks like diff notation accidentally included in the file content. This will display incorrectly in the rendered documentation.📝 Committable suggestion
🤖 Prompt for AI Agents