1 /* 2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 #include <SkImage.h> 18 #include <SkRuntimeEffect.h> 19 #include <SkShader.h> 20 #include "../compat/SkiaGpuContext.h" 21 namespace android { 22 namespace renderengine { 23 namespace skia { 24 /** 25 * MouriMap is a fast, albeit not realtime, tonemapping algorithm optimized for near-exact 26 * preservation of SDR (or, equivalently, LDR) regions, while trying to do an acceptable job of 27 * preserving HDR detail. 28 * 29 * MouriMap is a local tonemapping algorithm, meaning that nearby pixels are taken into 30 * consideration when choosing a tonemapping curve. 31 * 32 * The algorithm conceptually is as follows: 33 * 1. Partition the image into 128x128 chunks, computing the log2(maximum luminance) in each chunk 34 *. a. Maximum luminance is computed as max(R, G, B), where the R, G, B values are in linear 35 *. luminance on a scale defined by the destination color gamut. Max(R, G, B) has been found 36 *. to minimize difference in hue while restricting to typical LDR color volumes. See: Burke, 37 *. Adam & Smith, Michael & Zink, Michael. 2020. Color Volume and Hue-preservation in HDR 38 *. Tone Mapping. SMPTE Motion Imaging Journal. 39 *. b. Each computed luminance is lower-bounded by 1.0 in Skia's color 40 *. management, or 203 nits. 41 * 2. Blur the resulting chunks using a 5x5 gaussian kernel, to smooth out the local luminance map. 42 * 3. Now, for each pixel in the original image: 43 * a. Upsample from the blurred chunks of luminance computed in (2). Call this luminance value 44 *. L: an estimate of the maximum luminance of surrounding pixels. 45 *. b. If the luminance is less than 1.0 (203 nits), then do not modify the pixel value of the 46 *. original image. 47 *. c. Otherwise, 48 *. parameterize a tone-mapping curve using a method described by Chrome: 49 *. https://docs.google.com/document/d/17T2ek1i2R7tXdfHCnM-i5n6__RoYe0JyMfKmTEjoGR8/. 50 *. i. Compute a gain G = (1 + max(linear R, linear G, linear B) / (L * L)) 51 *. / (1 + max(linear R, linear G, linear B)). Note the similarity with the 1D curve 52 *. described by Erik Reinhard, Michael Stark, Peter Shirley, and James Ferwerda. 2002. 53 *. Photographic tone reproduction for digital images. ACM Trans. Graph. 54 *. ii. Multiply G by the linear source colors to compute the final colors. 55 * 56 * Because it is a multi-renderpass algorithm requiring multiple off-screen textures, MouriMap is 57 * typically not suitable to be ran "frequently", at high refresh rates (e.g., 120hz). However, 58 * MouriMap is sufficiently fast enough for infrequent composition where preserving SDR detail is 59 * most important, such as for screenshots. 60 */ 61 class MouriMap { 62 public: 63 MouriMap(); 64 // Apply the MouriMap tonemmaping operator to the input. 65 // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger 66 // then 1.0 means that there is headroom above the SDR region. 67 sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio); 68 69 private: 70 sk_sp<SkImage> downchunk(SkiaGpuContext* context, sk_sp<SkShader> input, 71 float hdrSdrRatio) const; 72 sk_sp<SkImage> blur(SkiaGpuContext* context, SkImage* input) const; 73 sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio) const; 74 const sk_sp<SkRuntimeEffect> mCrosstalkAndChunk16x16; 75 const sk_sp<SkRuntimeEffect> mChunk8x8; 76 const sk_sp<SkRuntimeEffect> mBlur; 77 const sk_sp<SkRuntimeEffect> mTonemap; 78 }; 79 } // namespace skia 80 } // namespace renderengine 81 } // namespace android