Skip to content

feat: 그림 밝기/대비 효과 SVG 반영 (#150)#395

Closed
oksure wants to merge 2 commits into
edwardkim:develfrom
oksure:contrib/image-brightness-contrast-v2
Closed

feat: 그림 밝기/대비 효과 SVG 반영 (#150)#395
oksure wants to merge 2 commits into
edwardkim:develfrom
oksure:contrib/image-brightness-contrast-v2

Conversation

@oksure

@oksure oksure commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

요약

기존 PR #387devel 기반으로 재작업한 PR 입니다. main 으로 직접 올린 점 죄송합니다.

  • ImageNodebrightness, contrast 필드 추가
  • SVG 렌더링 시 <feComponentTransfer> 필터로 밝기/대비 효과 적용
  • 기존 이미지 효과 필터와 <g filter> 로 중첩 조합

변경 파일 (7개)

  • src/renderer/svg.rs — 밝기/대비용 SVG 필터 defs 등록 + 이미지 렌더링 시 필터 래핑
  • src/renderer/render_tree.rsImageNode 에 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 — 경고 0

Closes #150

…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
Copilot AI review requested due to automatic review settings April 27, 2026 23:46

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ImageNode with brightness / contrast fields and default them to 0.
  • Propagate pic.image_attr.brightness/contrast through all ImageNode construction 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.

Comment thread src/renderer/svg.rs
Comment on lines +1220 to +1226
/// 밝기/대비 조정용 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;
}

Comment thread src/renderer/layout/shape_layout.rs Outdated
Comment on lines +962 to +963
brightness: pic.image_attr.brightness,
contrast: pic.image_attr.contrast,
Comment thread src/renderer/layout/paragraph_layout.rs Outdated
Comment on lines +1705 to +1706
brightness: pic.image_attr.brightness,
contrast: pic.image_attr.contrast,
Comment thread src/renderer/layout/paragraph_layout.rs Outdated
Comment on lines +1957 to +1958
brightness: pic.image_attr.brightness,
contrast: pic.image_attr.contrast,
Comment thread src/renderer/layout/paragraph_layout.rs Outdated
Comment on lines +2042 to +2043
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
@oksure

oksure commented Apr 28, 2026

Copy link
Copy Markdown
Contributor Author

Copilot 리뷰 피드백 반영했습니다 (e637687):

  1. 들여쓰기 정렬shape_layout.rs, paragraph_layout.rs 4곳의 brightness/contrast 필드 들여쓰기를 주변 필드와 동일하게 수정
  2. 단위 테스트 추가svg/tests.rsensure_brightness_contrast_filter() 테스트 3개:
    • zero 입력 시 None 반환
    • nonzero 입력 시 defs에 <feComponentTransfer> 필터 등록
    • 동일 파라미터 중복 호출 시 dedup 확인

cargo test 전체 통과, cargo clippy -- -D warnings 경고 0.

@edwardkim

Copy link
Copy Markdown
Owner

PR 처리 완료 안내 + 후속 개선 제안 한 가지 정리합니다.

처리 결과

옵션 C (cherry-pick + 메인테이너 후속 정정) 로 진행했습니다. local/devel 에 머지 + push 완료.

메인테이너 후속 정정 commit (`c9fe462`)

  1. 누락된 들여쓰기 정정 (`paragraph_layout.rs:1957-1958`) — 들여쓰기 정렬 commit (`e637687`) 에서 한 곳 누락된 부분 정정
  2. `ensure_brightness_contrast_filter` 의 `i8` clamp 추가 — HWP 스펙은 -100..=100 이지만 코드 레벨 가드 없어 손상된 입력 시 비정상 색이 될 수 있던 부분에 `.clamp(-100, 100)` 적용
  3. 수치 테스트 3개 추가 — slope/intercept 합성이 의도한 값과 일치하는지 검증 (b=50,c=0 → slope=1.0,intercept=0.5 / b=0,c=50 → slope=1.5,intercept=-0.25 / 범위 밖 입력 → clamp 검증)

검증

후속 개선 제안 — `HashSet` dedup

`SvgRenderer` 의 두 helper 가 `self.defs.iter().any(|d| d == &def)` 로 dedup 합니다:

  • `ensure_image_effect_filter` (기존, line 1206)
  • `ensure_brightness_contrast_filter` (본 PR 추가)

현재 규모에서는 문제 없지만, 필터 종류가 늘면 O(n) 비교 누적이 됩니다. ID 가 이미 unique 키이므로 `HashSet` 으로 ID 만 추적하면 O(1) 로 개선 가능:

```rust
struct SvgRenderer {
defs: Vec,
defs_ids: HashSet, // 신규 — ID 만 추적
// ...
}

fn ensure_xxx_filter(&mut self, ...) -> Option {
let id = format!("...");
if self.defs_ids.insert(id.clone()) {
self.defs.push(format!("<filter id=\"{id}\">..."));
}
Some(id)
}
```

본 PR 의 직접 정정 사항은 아니라 별도 task 후보로 남겨둡니다. 의향 있으시면 별도 PR 로 환영합니다 — `SvgRenderer.defs` 의 dedup 정책 통일 + 다른 `defs.push` 지점 (마커/그라디언트/패턴 등 7곳) 도 함께 정리하는 형태가 적절할 것입니다.

이 PR 은 close 합니다. 좋은 기여 감사합니다.

edwardkim added a commit that referenced this pull request Apr 28, 2026
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>
edwardkim added a commit that referenced this pull request Apr 28, 2026
- PR #395 (그림 밝기/대비, @oksure) 옵션 C cherry-pick + 메인테이너 후속 정정 완료
- 검토 / 처리 보고서 추가
- 오늘할일 갱신

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@edwardkim

Copy link
Copy Markdown
Owner

완료. `devel` 에 cherry-pick (작성자 attribution 보존) + 메인테이너 후속 정정으로 머지됨 (commit `94d9347`).

이슈 #150 의 brightness/contrast 부분이 적용됐고, 워터마크 효과는 후속 PR 후보로 OPEN 유지합니다.

머지 commit:

  • `f75bd66` (cherry-pick from cb248cd, @oksure) — feat: apply brightness/contrast image effects in SVG output
  • `f85d05f` (cherry-pick from e637687, @oksure) — fix: 들여쓰기 정렬 + 단위 테스트 3개
  • `c9fe462` — 메인테이너 후속 정정 (누락 들여쓰기 / i8 clamp / 수치 테스트 3개)

향후 `HashSet` dedup 개선 의향 있으시면 별도 PR 환영합니다 (위 댓글 참고).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants