Use the LUT to gamma correct text, and fix stem darkening math
This commit is contained in:
parent
82fd214a76
commit
e5b76726d9
|
@ -70,6 +70,10 @@
|
|||
{{>partials/switch.html id="pf-subpixel-aa"
|
||||
title="Subpixel AA"}}
|
||||
</div>
|
||||
<div class="form-group row justify-content-between">
|
||||
{{>partials/switch.html id="pf-gamma-correction"
|
||||
title="Gamma Correction"}}
|
||||
</div>
|
||||
<div class="form-group row justify-content-between">
|
||||
{{>partials/switch.html id="pf-stem-darkening"
|
||||
title="Stem Darkening"}}
|
||||
|
|
|
@ -22,6 +22,8 @@ export type DirectRenderingMode = 'none' | 'color' | 'color-depth';
|
|||
|
||||
export type SubpixelAAType = 'none' | 'medium';
|
||||
|
||||
export type GammaCorrectionMode = 'off' | 'on';
|
||||
|
||||
export type StemDarkeningMode = 'none' | 'dark';
|
||||
|
||||
export abstract class AntialiasingStrategy {
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
import {AntialiasingStrategyName, StemDarkeningMode, SubpixelAAType} from "./aa-strategy";
|
||||
import {AntialiasingStrategyName, GammaCorrectionMode, StemDarkeningMode} from "./aa-strategy";
|
||||
import {SubpixelAAType} from "./aa-strategy";
|
||||
import {FilePickerView} from "./file-picker";
|
||||
import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader';
|
||||
import {expectNotNull, unwrapNull, unwrapUndef} from './utils';
|
||||
|
@ -16,6 +17,52 @@ import {DemoView, Timings, TIMINGS} from "./view";
|
|||
|
||||
const GAMMA_LUT_URI: string = "/textures/gamma-lut.png";
|
||||
|
||||
const SWITCHES: SwitchMap = {
|
||||
gammaCorrection: {
|
||||
id: 'pf-gamma-correction',
|
||||
offValue: 'off',
|
||||
onValue: 'on',
|
||||
radioButtonName: 'gammaCorrectionRadioButton',
|
||||
},
|
||||
stemDarkening: {
|
||||
id: 'pf-stem-darkening',
|
||||
offValue: 'none',
|
||||
onValue: 'dark',
|
||||
radioButtonName: 'stemDarkeningRadioButton',
|
||||
},
|
||||
subpixelAA: {
|
||||
id: 'pf-subpixel-aa',
|
||||
offValue: 'none',
|
||||
onValue: 'medium',
|
||||
radioButtonName: 'subpixelAARadioButton',
|
||||
},
|
||||
};
|
||||
|
||||
interface SwitchDescriptor {
|
||||
id: string;
|
||||
radioButtonName: keyof Switches;
|
||||
onValue: string;
|
||||
offValue: string;
|
||||
}
|
||||
|
||||
interface SwitchMap {
|
||||
gammaCorrection: SwitchDescriptor;
|
||||
stemDarkening: SwitchDescriptor;
|
||||
subpixelAA: SwitchDescriptor;
|
||||
}
|
||||
|
||||
export interface AAOptions {
|
||||
gammaCorrection: GammaCorrectionMode;
|
||||
stemDarkening: StemDarkeningMode;
|
||||
subpixelAA: SubpixelAAType;
|
||||
}
|
||||
|
||||
interface Switches {
|
||||
subpixelAARadioButton: HTMLInputElement | null;
|
||||
gammaCorrectionRadioButton: HTMLInputElement | null;
|
||||
stemDarkeningRadioButton: HTMLInputElement | null;
|
||||
}
|
||||
|
||||
export abstract class AppController {
|
||||
protected canvas: HTMLCanvasElement;
|
||||
|
||||
|
@ -47,9 +94,14 @@ export abstract class AppController {
|
|||
protected abstract get defaultFile(): string;
|
||||
}
|
||||
|
||||
export abstract class DemoAppController<View extends DemoView> extends AppController {
|
||||
export abstract class DemoAppController<View extends DemoView> extends AppController
|
||||
implements Switches {
|
||||
view: Promise<View>;
|
||||
|
||||
subpixelAARadioButton: HTMLInputElement | null;
|
||||
gammaCorrectionRadioButton: HTMLInputElement | null;
|
||||
stemDarkeningRadioButton: HTMLInputElement | null;
|
||||
|
||||
protected abstract readonly builtinFileURI: string;
|
||||
|
||||
protected filePickerView: FilePickerView | null;
|
||||
|
@ -59,8 +111,6 @@ export abstract class DemoAppController<View extends DemoView> extends AppContro
|
|||
protected gammaLUT: HTMLImageElement;
|
||||
|
||||
private aaLevelSelect: HTMLSelectElement | null;
|
||||
private subpixelAARadioButton: HTMLInputElement | null;
|
||||
private stemDarkeningRadioButton: HTMLInputElement | null;
|
||||
private fpsLabel: HTMLElement | null;
|
||||
|
||||
constructor() {
|
||||
|
@ -169,22 +219,15 @@ export abstract class DemoAppController<View extends DemoView> extends AppContro
|
|||
// click listener that Bootstrap sets up until the event bubbles up to the document. This
|
||||
// click listener is what toggles the `checked` attribute, so we have to wait until it
|
||||
// fires before updating the antialiasing settings.
|
||||
this.subpixelAARadioButton =
|
||||
document.getElementById('pf-subpixel-aa-select-on') as HTMLInputElement | null;
|
||||
const subpixelAAButtons =
|
||||
document.getElementById('pf-subpixel-aa-buttons') as HTMLElement | null;
|
||||
if (subpixelAAButtons != null) {
|
||||
subpixelAAButtons.addEventListener('click', () => {
|
||||
window.setTimeout(() => this.updateAALevel(), 0);
|
||||
}, false);
|
||||
}
|
||||
|
||||
this.stemDarkeningRadioButton =
|
||||
document.getElementById('pf-stem-darkening-select-on') as HTMLInputElement | null;
|
||||
const stemDarkeningButtons =
|
||||
document.getElementById('pf-stem-darkening-buttons') as HTMLElement | null;
|
||||
if (stemDarkeningButtons != null) {
|
||||
stemDarkeningButtons.addEventListener('click', () => {
|
||||
for (const switchName of Object.keys(SWITCHES) as Array<keyof SwitchMap>) {
|
||||
const radioButtonName = SWITCHES[switchName].radioButtonName;
|
||||
const switchID = SWITCHES[switchName].id;
|
||||
this[radioButtonName] = document.getElementById(`${switchID}-select-on`) as
|
||||
HTMLInputElement | null;
|
||||
const buttons = document.getElementById(`${switchID}-buttons`) as HTMLElement | null;
|
||||
if (buttons == null)
|
||||
continue;
|
||||
buttons.addEventListener('click', () => {
|
||||
window.setTimeout(() => this.updateAALevel(), 0);
|
||||
}, false);
|
||||
}
|
||||
|
@ -242,20 +285,19 @@ export abstract class DemoAppController<View extends DemoView> extends AppContro
|
|||
aaLevel = 0;
|
||||
}
|
||||
|
||||
let subpixelAA: SubpixelAAType;
|
||||
if (this.subpixelAARadioButton != null && this.subpixelAARadioButton.checked)
|
||||
subpixelAA = 'medium';
|
||||
else
|
||||
subpixelAA = 'none';
|
||||
|
||||
let stemDarkening: StemDarkeningMode;
|
||||
if (this.stemDarkeningRadioButton != null && this.stemDarkeningRadioButton.checked)
|
||||
stemDarkening = 'dark';
|
||||
else
|
||||
stemDarkening = 'none';
|
||||
const aaOptions: Partial<AAOptions> = {};
|
||||
for (const switchName of Object.keys(SWITCHES) as Array<keyof SwitchMap>) {
|
||||
const switchDescriptor = SWITCHES[switchName];
|
||||
const radioButtonName = switchDescriptor.radioButtonName;
|
||||
const radioButton = this[radioButtonName];
|
||||
if (radioButton != null && radioButton.checked)
|
||||
aaOptions[switchName] = switchDescriptor.onValue as any;
|
||||
else
|
||||
aaOptions[switchName] = switchDescriptor.offValue as any;
|
||||
}
|
||||
|
||||
this.view.then(view => {
|
||||
view.setAntialiasingOptions(aaType, aaLevel, subpixelAA, stemDarkening);
|
||||
view.setAntialiasingOptions(aaType, aaLevel, aaOptions as AAOptions);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
import * as glmatrix from 'gl-matrix';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from './aa-strategy';
|
||||
import {StemDarkeningMode, SubpixelAAType} from './aa-strategy';
|
||||
import {AntialiasingStrategy, AntialiasingStrategyName, GammaCorrectionMode} from './aa-strategy';
|
||||
import {NoAAStrategy, StemDarkeningMode, SubpixelAAType} from './aa-strategy';
|
||||
import {AAOptions} from './app-controller';
|
||||
import PathfinderBufferTexture from "./buffer-texture";
|
||||
import {UniformMap} from './gl-utils';
|
||||
import {PathfinderMeshBuffers, PathfinderMeshData} from "./meshes";
|
||||
|
@ -42,8 +43,8 @@ export abstract class Renderer {
|
|||
return glmatrix.vec2.create();
|
||||
}
|
||||
|
||||
get bgColor(): glmatrix.vec4 | null {
|
||||
return null;
|
||||
get bgColor(): glmatrix.vec4 {
|
||||
return glmatrix.vec4.clone([1.0, 1.0, 1.0, 1.0]);
|
||||
}
|
||||
|
||||
get fgColor(): glmatrix.vec4 | null {
|
||||
|
@ -66,6 +67,8 @@ export abstract class Renderer {
|
|||
protected lastTimings: Timings;
|
||||
protected pathColorsBufferTextures: PathfinderBufferTexture[];
|
||||
|
||||
protected gammaCorrectionMode: GammaCorrectionMode;
|
||||
|
||||
protected get pathIDsAreInstanced(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
@ -87,6 +90,8 @@ export abstract class Renderer {
|
|||
|
||||
this.lastTimings = { rendering: 0, compositing: 0 };
|
||||
|
||||
this.gammaCorrectionMode = 'on';
|
||||
|
||||
this.pathTransformBufferTextures = [];
|
||||
this.pathColorsBufferTextures = [];
|
||||
|
||||
|
@ -154,12 +159,14 @@ export abstract class Renderer {
|
|||
|
||||
setAntialiasingOptions(aaType: AntialiasingStrategyName,
|
||||
aaLevel: number,
|
||||
subpixelAA: SubpixelAAType,
|
||||
stemDarkening: StemDarkeningMode) {
|
||||
aaOptions: AAOptions):
|
||||
void {
|
||||
this.gammaCorrectionMode = aaOptions.gammaCorrection;
|
||||
|
||||
this.antialiasingStrategy = this.createAAStrategy(aaType,
|
||||
aaLevel,
|
||||
subpixelAA,
|
||||
stemDarkening);
|
||||
aaOptions.subpixelAA,
|
||||
aaOptions.stemDarkening);
|
||||
|
||||
this.antialiasingStrategy.init(this);
|
||||
if (this.meshData != null)
|
||||
|
@ -168,12 +175,12 @@ export abstract class Renderer {
|
|||
this.renderContext.setDirty();
|
||||
}
|
||||
|
||||
canvasResized() {
|
||||
canvasResized(): void {
|
||||
if (this.antialiasingStrategy != null)
|
||||
this.antialiasingStrategy.init(this);
|
||||
}
|
||||
|
||||
setFramebufferSizeUniform(uniforms: UniformMap) {
|
||||
setFramebufferSizeUniform(uniforms: UniformMap): void {
|
||||
const gl = this.renderContext.gl;
|
||||
gl.uniform2i(uniforms.uFramebufferSize,
|
||||
this.destAllocatedSize[0],
|
||||
|
@ -201,7 +208,7 @@ export abstract class Renderer {
|
|||
gl.uniform2f(uniforms.uTexScale, usedSize[0], usedSize[1]);
|
||||
}
|
||||
|
||||
setTransformSTUniform(uniforms: UniformMap, objectIndex: number) {
|
||||
setTransformSTUniform(uniforms: UniformMap, objectIndex: number): void {
|
||||
// FIXME(pcwalton): Lossy conversion from a 4x4 matrix to an ST matrix is ugly and fragile.
|
||||
// Refactor.
|
||||
const renderContext = this.renderContext;
|
||||
|
@ -219,7 +226,7 @@ export abstract class Renderer {
|
|||
transform[13]);
|
||||
}
|
||||
|
||||
uploadPathColors(objectCount: number) {
|
||||
uploadPathColors(objectCount: number): void {
|
||||
const renderContext = this.renderContext;
|
||||
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
|
||||
const pathColors = this.pathColorsForObject(objectIndex);
|
||||
|
@ -237,7 +244,7 @@ export abstract class Renderer {
|
|||
}
|
||||
}
|
||||
|
||||
uploadPathTransforms(objectCount: number) {
|
||||
uploadPathTransforms(objectCount: number): void {
|
||||
const renderContext = this.renderContext;
|
||||
for (let objectIndex = 0; objectIndex < objectCount; objectIndex++) {
|
||||
const pathTransforms = this.pathTransformsForObject(objectIndex);
|
||||
|
@ -273,6 +280,18 @@ export abstract class Renderer {
|
|||
return glmatrix.vec4.create();
|
||||
}
|
||||
|
||||
protected bindGammaLUT(bgColor: glmatrix.vec3, textureUnit: number, uniforms: UniformMap):
|
||||
void {
|
||||
const renderContext = this.renderContext;
|
||||
const gl = renderContext.gl;
|
||||
|
||||
gl.activeTexture(gl.TEXTURE0 + textureUnit);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.gammaLUTTexture);
|
||||
gl.uniform1i(uniforms.uGammaLUT, textureUnit);
|
||||
|
||||
gl.uniform3f(uniforms.uBGColor, bgColor[0], bgColor[1], bgColor[2]);
|
||||
}
|
||||
|
||||
protected abstract createAAStrategy(aaType: AntialiasingStrategyName,
|
||||
aaLevel: number,
|
||||
subpixelAA: SubpixelAAType,
|
||||
|
@ -515,6 +534,12 @@ export abstract class Renderer {
|
|||
const texture = unwrapNull(gl.createTexture());
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, gl.LUMINANCE, gl.UNSIGNED_BYTE, gammaLUT);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
|
||||
this.gammaLUTTexture = texture;
|
||||
}
|
||||
|
||||
private initImplicitCoverCurveVAO(objectIndex: number, instanceRange: Range): void {
|
||||
|
|
|
@ -12,7 +12,8 @@ import {AttributeMap, UniformMap} from './gl-utils';
|
|||
import {expectNotNull, PathfinderError, unwrapNull} from './utils';
|
||||
|
||||
export interface ShaderMap<T> {
|
||||
blit: T;
|
||||
blitLinear: T;
|
||||
blitGamma: T;
|
||||
compositeAlphaMask: T;
|
||||
demo3DDistantGlyph: T;
|
||||
demo3DMonument: T;
|
||||
|
@ -43,7 +44,8 @@ export interface UnlinkedShaderProgram {
|
|||
const COMMON_SHADER_URL: string = '/glsl/gles2/common.inc.glsl';
|
||||
|
||||
export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
|
||||
'blit',
|
||||
'blitLinear',
|
||||
'blitGamma',
|
||||
'compositeAlphaMask',
|
||||
'directCurve',
|
||||
'directInterior',
|
||||
|
@ -67,8 +69,12 @@ export const SHADER_NAMES: Array<keyof ShaderMap<void>> = [
|
|||
];
|
||||
|
||||
const SHADER_URLS: ShaderMap<ShaderProgramURLs> = {
|
||||
blit: {
|
||||
fragment: "/glsl/gles2/blit.fs.glsl",
|
||||
blitGamma: {
|
||||
fragment: "/glsl/gles2/blit-gamma.fs.glsl",
|
||||
vertex: "/glsl/gles2/blit.vs.glsl",
|
||||
},
|
||||
blitLinear: {
|
||||
fragment: "/glsl/gles2/blit-linear.fs.glsl",
|
||||
vertex: "/glsl/gles2/blit.vs.glsl",
|
||||
},
|
||||
compositeAlphaMask: {
|
||||
|
|
|
@ -176,7 +176,7 @@ export default class SSAAStrategy extends AntialiasingStrategy {
|
|||
if (this.subpixelAA !== 'none')
|
||||
resolveProgram = renderContext.shaderPrograms.ssaaSubpixelResolve;
|
||||
else
|
||||
resolveProgram = renderContext.shaderPrograms.blit;
|
||||
resolveProgram = renderContext.shaderPrograms.blitLinear;
|
||||
gl.useProgram(resolveProgram.program);
|
||||
renderContext.initQuadVAO(resolveProgram.attributes);
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ import * as opentype from 'opentype.js';
|
|||
import {Metrics} from 'opentype.js';
|
||||
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
|
||||
import {StemDarkeningMode, SubpixelAAType} from './aa-strategy';
|
||||
import {DemoAppController} from './app-controller';
|
||||
import {AAOptions, DemoAppController} from './app-controller';
|
||||
import {Atlas, ATLAS_SIZE, AtlasGlyph, GlyphKey, SUBPIXEL_GRANULARITY} from './atlas';
|
||||
import PathfinderBufferTexture from './buffer-texture';
|
||||
import {CameraView, OrthographicCamera} from "./camera";
|
||||
|
@ -379,10 +379,9 @@ class TextDemoRenderer extends TextRenderer {
|
|||
|
||||
setAntialiasingOptions(aaType: AntialiasingStrategyName,
|
||||
aaLevel: number,
|
||||
subpixelAA: SubpixelAAType,
|
||||
stemDarkening: StemDarkeningMode):
|
||||
aaOptions: AAOptions):
|
||||
void {
|
||||
super.setAntialiasingOptions(aaType, aaLevel, subpixelAA, stemDarkening);
|
||||
super.setAntialiasingOptions(aaType, aaLevel, aaOptions);
|
||||
|
||||
// Need to relayout because changing AA options can cause font dilation to change...
|
||||
this.layoutText();
|
||||
|
@ -425,8 +424,11 @@ class TextDemoRenderer extends TextRenderer {
|
|||
gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
// Set the appropriate program.
|
||||
const programName = this.gammaCorrectionMode === 'off' ? 'blitLinear' : 'blitGamma';
|
||||
const blitProgram = this.renderContext.shaderPrograms[programName];
|
||||
|
||||
// Set up the composite VAO.
|
||||
const blitProgram = this.renderContext.shaderPrograms.blit;
|
||||
const attributes = blitProgram.attributes;
|
||||
gl.useProgram(blitProgram.program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.glyphPositionsBuffer);
|
||||
|
@ -458,6 +460,7 @@ class TextDemoRenderer extends TextRenderer {
|
|||
gl.bindTexture(gl.TEXTURE_2D, destTexture);
|
||||
gl.uniform1i(blitProgram.uniforms.uSource, 0);
|
||||
this.setIdentityTexScaleUniform(blitProgram.uniforms);
|
||||
this.bindGammaLUT(glmatrix.vec3.clone([1.0, 1.0, 1.0]), 1, blitProgram.uniforms);
|
||||
const totalGlyphCount = this.layout.textFrame.totalGlyphCount;
|
||||
gl.drawElements(gl.TRIANGLES, totalGlyphCount * 6, gl.UNSIGNED_INT, 0);
|
||||
}
|
||||
|
|
|
@ -386,10 +386,7 @@ export function computeStemDarkeningAmount(pixelsPerEm: number, pixelsPerUnit: n
|
|||
return amount;
|
||||
|
||||
glmatrix.vec2.scale(amount, STEM_DARKENING_FACTORS, pixelsPerEm);
|
||||
glmatrix.vec2.min(amount, amount, MIN_STEM_DARKENING_AMOUNT);
|
||||
|
||||
// Convert diameter to radius.
|
||||
glmatrix.vec2.scale(amount, amount, 0.5);
|
||||
glmatrix.vec2.max(amount, amount, MIN_STEM_DARKENING_AMOUNT);
|
||||
glmatrix.vec2.scale(amount, amount, 1.0 / pixelsPerUnit);
|
||||
return amount;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import * as glmatrix from 'gl-matrix';
|
|||
|
||||
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
|
||||
import {StemDarkeningMode, SubpixelAAType} from "./aa-strategy";
|
||||
import {AAOptions} from './app-controller';
|
||||
import PathfinderBufferTexture from './buffer-texture';
|
||||
import {Camera} from "./camera";
|
||||
import {EXTDisjointTimerQuery, QUAD_ELEMENTS, UniformMap} from './gl-utils';
|
||||
|
@ -153,15 +154,13 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
|
|||
meshData: PathfinderMeshData[];
|
||||
|
||||
get colorAlphaFormat(): GLenum {
|
||||
return this.sRGBExt == null ? this.gl.RGBA : this.sRGBExt.SRGB_ALPHA_EXT;
|
||||
return this.gl.RGBA;
|
||||
}
|
||||
|
||||
get renderContext(): RenderContext {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected sRGBExt: EXTsRGB;
|
||||
|
||||
protected colorBufferHalfFloatExt: any;
|
||||
|
||||
private wantsScreenshot: boolean;
|
||||
|
@ -204,9 +203,9 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
|
|||
|
||||
setAntialiasingOptions(aaType: AntialiasingStrategyName,
|
||||
aaLevel: number,
|
||||
subpixelAA: SubpixelAAType,
|
||||
stemDarkening: StemDarkeningMode) {
|
||||
this.renderer.setAntialiasingOptions(aaType, aaLevel, subpixelAA, stemDarkening);
|
||||
aaOptions: AAOptions):
|
||||
void {
|
||||
this.renderer.setAntialiasingOptions(aaType, aaLevel, aaOptions);
|
||||
}
|
||||
|
||||
protected resized(): void {
|
||||
|
@ -220,7 +219,6 @@ export abstract class DemoView extends PathfinderView implements RenderContext {
|
|||
"Failed to initialize WebGL! Check that your browser supports it.");
|
||||
this.colorBufferHalfFloatExt = this.gl.getExtension('EXT_color_buffer_half_float');
|
||||
this.instancedArraysExt = this.gl.getExtension('ANGLE_instanced_arrays');
|
||||
this.sRGBExt = this.gl.getExtension('EXT_sRGB');
|
||||
this.textureHalfFloatExt = this.gl.getExtension('OES_texture_half_float');
|
||||
this.timerQueryExt = this.gl.getExtension('EXT_disjoint_timer_query');
|
||||
this.vertexArrayObjectExt = this.gl.getExtension('OES_vertex_array_object');
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// pathfinder/shaders/gles2/blit-gamma.fs.glsl
|
||||
//
|
||||
// Copyright (c) 2017 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// Blits a texture, applying gamma correction.
|
||||
|
||||
precision mediump float;
|
||||
|
||||
uniform sampler2D uSource;
|
||||
|
||||
uniform vec3 uBGColor;
|
||||
uniform sampler2D uGammaLUT;
|
||||
|
||||
varying vec2 vTexCoord;
|
||||
|
||||
void main() {
|
||||
vec4 source = texture2D(uSource, vTexCoord);
|
||||
gl_FragColor = vec4(gammaCorrect(source.rgb, uBGColor, uGammaLUT), source.a);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// pathfinder/shaders/gles2/blit.fs.glsl
|
||||
// pathfinder/shaders/gles2/blit-linear.fs.glsl
|
||||
//
|
||||
// Copyright (c) 2017 The Pathfinder Project Developers.
|
||||
//
|
|
@ -306,6 +306,17 @@ float lcdFilter(float shadeL2, float shadeL1, float shade0, float shadeR1, float
|
|||
LCD_FILTER_FACTOR_2 * shadeR2;
|
||||
}
|
||||
|
||||
float gammaCorrectChannel(float fgColor, float bgColor, sampler2D gammaLUT) {
|
||||
return texture2D(gammaLUT, vec2(fgColor, 1.0 - bgColor)).r;
|
||||
}
|
||||
|
||||
// `fgColor` is in linear space.
|
||||
vec3 gammaCorrect(vec3 fgColor, vec3 bgColor, sampler2D gammaLUT) {
|
||||
return vec3(gammaCorrectChannel(fgColor.r, bgColor.r, gammaLUT),
|
||||
gammaCorrectChannel(fgColor.g, bgColor.g, gammaLUT),
|
||||
gammaCorrectChannel(fgColor.b, bgColor.b, gammaLUT));
|
||||
}
|
||||
|
||||
int unpackUInt16(vec2 packedValue) {
|
||||
ivec2 valueBytes = ivec2(floor(packedValue * 255.0));
|
||||
return valueBytes.y * 256 + valueBytes.x;
|
||||
|
|
Loading…
Reference in New Issue