← All issues

Non-sRGB Gradient Rendering via Sampled CGGradient

5e93c4d

Source/WebCore/platform/graphics/SampledGradientBuilder.h

+static void bisectAndCollectStops(
+ EvaluateCallback evaluate, const SamplingData& data,
+ float offset0, ColorComponents<float, 4> color0,
+ float offset1, ColorComponents<float, 4> color1,
+ Vector<float>& locations, Vector<float>& components)
+{
+ static constexpr float minimumSegmentWidth = 1.0f / 2048.0f;
+ if (offset1 - offset0 < minimumSegmentWidth)
+ return;
+
+ float midOffset = (offset0 + offset1) * 0.5f;
+ auto colorMid = evaluate(data, midOffset);
+ ...
+ static constexpr float tolerance = 8.0f / 255.0f;
+ if (maxDiff <= tolerance)
+ return;

CSS gradient는 color interpolation space를 지정할 수 있습니다. 웹 플랫폼이 OKLab을 채택한 이후, CoreGraphics가 sRGB만 네이티브로 지원하기 때문에 WebKit이 직접 interpolation을 수행해야 합니다. 기존 방식은 CGShading을 사용했습니다. 이는 callback 기반 API로, CoreGraphics가 scanline마다 WebKit을 호출해 색상을 가져오는 구조입니다. 결과적으로 매 프레임 draw 시점에 interpolation 비용이 반복적으로 발생했습니다. 반면 CGGradient는 pre-baked 방식으로, discrete color stop을 공급하면 CoreGraphics가 내부적으로 linear interpolation을 처리합니다.

이 commit은 새로운 SampledGradientBuilder를 통해 두 방식의 간극을 해소합니다. OKLab curve를 충분한 수의 linear segment로 pre-sampling하여, CGGradient의 linear interpolation이 실제 curve와 시각적으로 구별되지 않도록 합니다. adaptive bisection 알고리즘은 stop 범위를 재귀적으로 분할합니다. 각 구간의 중간 지점 색상을 계산해 linear interpolation 값과 비교하고, 차이가 8/255 tolerance를 초과하면 더 분할합니다. 최소 segment 폭은 1/2048입니다. 생성된 CGGradient 객체는 ColorInterpolationMethod와 stop vector를 key로 LRU cache에 저장됩니다.

Before (per draw):                    After (upfront + cached):
  CSS OKLab gradient                    CSS OKLab gradient
        │                                     │
        ▼                                     ▼
  CGShading (callback)                  SampledGradientBuilder
        │ ◄── called per scanline         └─ bisect until Δ < tolerance
        │     WebKit interpolates here    └─ emit N sRGB stops
        ▼                                     │
  CGContextDrawShading()                CGGradient ◄── LRU cache
                                              │
                                        CGContextDraw{Linear,Radial,Conic}Gradient()

draw 시점의 반복적인 colorstop interpolation을 제거함으로써 MotionMark 1.4 Chess subtest 성능이 직접적으로 개선됩니다. 매 프레임마다 발생하던 callback overhead가 일회성 pre-sampling으로 대체됩니다. SampledGradientBuilder는 342줄 규모의 floating-point geometry 및 caching 로직을 hot rendering path에 추가합니다.

🔒

Several edge cases in the new adaptive bisection and cache keying logic are worth security investigation.

더 확인하려면 구독해 주세요