feat: 그림 밝기/대비 효과 SVG 반영 (#150)#395
Conversation
…m#150) ImageAttr.brightness and contrast were already parsed from HWP/HWPX but never passed to the render tree or applied in SVG output. Changes: - Add brightness/contrast fields to ImageNode (render tree) - Pass values from ImageAttr through all 8 construction sites - Generate feComponentTransfer SVG filter for brightness/contrast - Combine with existing effect filters (grayscale/monochrome) via nested <g filter> wrappers The filter uses linear slope/intercept: - slope = (100 + contrast) / 100 - intercept = (0.5 - 0.5 * slope) + brightness / 100 Closes edwardkim#150
There was a problem hiding this comment.
Pull request overview
Adds SVG rendering support for picture brightness/contrast by carrying the parsed ImageAttr values through the render tree into the SVG renderer and applying them via an SVG <feComponentTransfer> filter (composable with existing grayscale/black-white effects).
Changes:
- Extend
ImageNodewithbrightness/contrastfields and default them to0. - Propagate
pic.image_attr.brightness/contrastthrough allImageNodeconstruction paths touched by layout. - Register and apply a brightness/contrast SVG filter (
<feComponentTransfer>) and nest it with existing image effect filters via<g filter="...">.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/renderer/svg.rs |
Adds defs generation + <g filter> wrapping to apply brightness/contrast in SVG output. |
src/renderer/render_tree.rs |
Adds brightness/contrast to ImageNode with defaults. |
src/renderer/layout/table_cell_content.rs |
Passes brightness/contrast when creating cell-internal picture ImageNodes. |
src/renderer/layout/shape_layout.rs |
Passes brightness/contrast for group-child Picture shapes. |
src/renderer/layout/picture_footnote.rs |
Passes brightness/contrast for header/footer picture rendering paths. |
src/renderer/layout/paragraph_layout.rs |
Passes brightness/contrast in inline picture (TAC) rendering paths. |
src/renderer/layout.rs |
Passes brightness/contrast for the paragraph_layout-missed fallback path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// 밝기/대비 조정용 SVG 필터를 defs에 보장하고 ID를 반환한다. | ||
| /// 둘 다 0이면 필터 불필요 → None 반환. | ||
| fn ensure_brightness_contrast_filter(&mut self, brightness: i8, contrast: i8) -> Option<String> { | ||
| if brightness == 0 && contrast == 0 { | ||
| return None; | ||
| } | ||
|
|
| brightness: pic.image_attr.brightness, | ||
| contrast: pic.image_attr.contrast, |
| brightness: pic.image_attr.brightness, | ||
| contrast: pic.image_attr.contrast, |
| brightness: pic.image_attr.brightness, | ||
| contrast: pic.image_attr.contrast, |
| brightness: pic.image_attr.brightness, | ||
| contrast: pic.image_attr.contrast, |
- ImageNode brightness/contrast 필드 들여쓰기를 주변 필드와 동일하게 정렬 (shape_layout.rs, paragraph_layout.rs 4곳) - ensure_brightness_contrast_filter() 단위 테스트 3개 추가: zero 입력 시 None, nonzero 시 defs 등록, 중복 호출 시 dedup
|
Copilot 리뷰 피드백 반영했습니다 (e637687):
|
|
PR 처리 완료 안내 + 후속 개선 제안 한 가지 정리합니다. 처리 결과옵션 C (cherry-pick + 메인테이너 후속 정정) 로 진행했습니다. local/devel 에 머지 + push 완료. 메인테이너 후속 정정 commit (`c9fe462`)
검증
후속 개선 제안 — `HashSet` dedup`SvgRenderer` 의 두 helper 가 `self.defs.iter().any(|d| d == &def)` 로 dedup 합니다:
현재 규모에서는 문제 없지만, 필터 종류가 늘면 O(n) 비교 누적이 됩니다. ID 가 이미 unique 키이므로 `HashSet` 으로 ID 만 추적하면 O(1) 로 개선 가능: ```rust fn ensure_xxx_filter(&mut self, ...) -> Option { 본 PR 의 직접 정정 사항은 아니라 별도 task 후보로 남겨둡니다. 의향 있으시면 별도 PR 로 환영합니다 — `SvgRenderer.defs` 의 dedup 정책 통일 + 다른 `defs.push` 지점 (마커/그라디언트/패턴 등 7곳) 도 함께 정리하는 형태가 적절할 것입니다. 이 PR 은 close 합니다. 좋은 기여 감사합니다. |
PR #395 (@oksure) 머지 후 메인테이너 후속 정정: 1. 누락된 들여쓰기 정정 (paragraph_layout.rs:1957-1958) - 작성자의 들여쓰기 정렬 commit (e637687) 이 한 곳 누락 - 40 칸 → 36 칸으로 주변 라인과 일치 2. ensure_brightness_contrast_filter 의 i8 clamp 추가 (svg.rs) - HWP 스펙: brightness/contrast 는 -100..=100 - 손상된 입력 (i8 max/min 등) 에 대비해 .clamp(-100, 100) 적용 - 잘못된 값으로 SVG 색이 비정상이 되는 경우 차단 3. 수치 테스트 3개 추가 (svg/tests.rs) - test_brightness_contrast_filter_pure_brightness: b=50, c=0 → slope=1.0, intercept=0.5 - test_brightness_contrast_filter_pure_contrast: b=0, c=50 → slope=1.5, intercept=-0.25 - test_brightness_contrast_filter_clamp_out_of_range: i8 max/min → -100..=100 clamp 검증 검증: - cargo test --lib: 1029 passed (1023 → +6, 작성자 3 + 메인테이너 3) - cargo test --test issue_418: 1/1 passed (Task #418 회귀 방지 보존) - cargo test --test svg_snapshot: 6/6 passed - cargo clippy --lib -- -D warnings: warning 0건 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
완료. `devel` 에 cherry-pick (작성자 attribution 보존) + 메인테이너 후속 정정으로 머지됨 (commit `94d9347`). 이슈 #150 의 brightness/contrast 부분이 적용됐고, 워터마크 효과는 후속 PR 후보로 OPEN 유지합니다. 머지 commit:
향후 `HashSet` dedup 개선 의향 있으시면 별도 PR 환영합니다 (위 댓글 참고). |
요약
기존 PR #387 을
devel기반으로 재작업한 PR 입니다.main으로 직접 올린 점 죄송합니다.ImageNode에brightness,contrast필드 추가<feComponentTransfer>필터로 밝기/대비 효과 적용<g filter>로 중첩 조합변경 파일 (7개)
src/renderer/svg.rs— 밝기/대비용 SVG 필터 defs 등록 + 이미지 렌더링 시 필터 래핑src/renderer/render_tree.rs—ImageNode에 brightness/contrast 필드 추가src/renderer/layout/table_cell_content.rs— 표 셀 내 Picture 생성 시 전달src/renderer/layout/shape_layout.rs— 그룹 내 Picture 생성 시 전달src/renderer/layout/picture_footnote.rs— 머리말/꼬리말 Picture 생성 시 전달src/renderer/layout/paragraph_layout.rs— 인라인 Picture(TAC) 경로에 전달src/renderer/layout.rs— paragraph_layout 미호출 케이스에서 전달테스트
cargo test— 전체 통과cargo clippy -- -D warnings— 경고 0Closes #150