Add a function changeLightness in order to change the lightness of a RGB color

This commit is contained in:
Calixte Denizet 2025-08-26 22:04:57 +02:00
parent f110e57512
commit 0ec503e5ef
7 changed files with 74 additions and 39 deletions

View File

@ -37,7 +37,11 @@ import {
Util,
warn,
} from "../shared/util.js";
import { PDFDateString, setLayerDimensions } from "./display_utils.js";
import {
changeLightness,
PDFDateString,
setLayerDimensions,
} from "./display_utils.js";
import { AnnotationStorage } from "./annotation_storage.js";
import { ColorConverters } from "../shared/scripting_utils.js";
import { DOMSVGFactory } from "./svg_factory.js";
@ -232,46 +236,13 @@ class AnnotationElement {
const opacity = this.data.opacity ?? 1;
const oppositeOpacity = 255 * (1 - opacity);
return this.#changeLightness(
return changeLightness(
Math.min(r + oppositeOpacity, 255),
Math.min(g + oppositeOpacity, 255),
Math.min(b + oppositeOpacity, 255)
);
}
#changeLightness(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
const newL = (((1 + Math.sqrt(l)) / 2) * 100).toFixed(2);
if (max === min) {
// gray
return `hsl(0, 0%, ${newL}%)`;
}
const d = max - min;
// hue (branch on max only; avoids mod)
let h;
if (max === r) {
h = (g - b) / d + (g < b ? 6 : 0);
} else if (max === g) {
h = (b - r) / d + 2;
} else {
// max === b
h = (r - g) / d + 4;
}
h = (h * 60).toFixed(2);
const s = ((d / (1 - Math.abs(2 * l - 1))) * 100).toFixed(2);
return `hsl(${h}, ${s}%, ${newL}%)`;
}
_normalizePoint(point) {
const {
page: { view },

View File

@ -770,7 +770,41 @@ const SupportedImageMimeTypes = [
"image/x-icon",
];
function changeLightness(r, g, b, lumCallback = l => (1 + Math.sqrt(l)) / 2) {
r /= 255;
g /= 255;
b /= 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const l = (max + min) / 2;
const newL = (lumCallback(l) * 100).toFixed(2);
if (max === min) {
// gray
return `hsl(0, 0%, ${newL}%)`;
}
const d = max - min;
// hue (branch on max only; avoids mod)
let h;
if (max === r) {
h = (g - b) / d + (g < b ? 6 : 0);
} else if (max === g) {
h = (b - r) / d + 2;
} else {
// max === b
h = (r - g) / d + 4;
}
h = (h * 60).toFixed(2);
const s = ((d / (1 - Math.abs(2 * l - 1))) * 100).toFixed(2);
return `hsl(${h}, ${s}%, ${newL}%)`;
}
export {
changeLightness,
deprecated,
fetchData,
getColorValues,

View File

@ -52,6 +52,7 @@ import {
version,
} from "./display/api.js";
import {
changeLightness,
fetchData,
getFilenameFromUrl,
getPdfFilenameFromUrl,
@ -98,6 +99,7 @@ globalThis.pdfjsLib = {
AnnotationMode,
AnnotationType,
build,
changeLightness,
ColorPicker,
createValidAbsoluteUrl,
DOMSVGFactory,
@ -153,6 +155,7 @@ export {
AnnotationMode,
AnnotationType,
build,
changeLightness,
ColorPicker,
createValidAbsoluteUrl,
DOMSVGFactory,

View File

@ -14,12 +14,14 @@
*/
import {
changeLightness,
getFilenameFromUrl,
getPdfFilenameFromUrl,
getRGB,
isValidFetchUrl,
PDFDateString,
} from "../../src/display/display_utils.js";
import { toBase64Util } from "../../src/shared/util.js";
import { isNodeJS, toBase64Util } from "../../src/shared/util.js";
describe("display_utils", function () {
describe("getFilenameFromUrl", function () {
@ -300,4 +302,26 @@ describe("display_utils", function () {
});
});
});
describe("changeLightness", function () {
it("Check that the lightness is changed correctly", function () {
if (isNodeJS) {
pending("DOM is not supported in Node.js.");
}
const div = document.createElement("div");
const { style } = div;
style.width = style.height = "0";
style.backgroundColor = "hsl(123, 45%, 67%)";
document.body.append(div);
const [r, g, b] = getRGB(getComputedStyle(div).backgroundColor);
div.remove();
expect([r, g, b]).toEqual([133, 209, 137]);
expect(changeLightness(r, g, b, l => l)).toEqual(
"hsl(123.16, 45.24%, 67.06%)"
);
expect(changeLightness(r, g, b, l => l / 2)).toEqual(
"hsl(123.16, 45.24%, 33.53%)"
);
});
});
});

View File

@ -43,6 +43,7 @@ import {
version,
} from "../../src/display/api.js";
import {
changeLightness,
fetchData,
getFilenameFromUrl,
getPdfFilenameFromUrl,
@ -82,6 +83,7 @@ const expectedAPI = Object.freeze({
AnnotationMode,
AnnotationType,
build,
changeLightness,
ColorPicker,
createValidAbsoluteUrl,
DOMSVGFactory,

View File

@ -15,6 +15,7 @@
import {
AnnotationEditorType,
changeLightness,
getRGB,
noContextMenu,
PDFDateString,
@ -355,9 +356,7 @@ class CommentManager {
return null; // No color provided.
}
const [r, g, b] = getRGB(color);
const gray = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
const ratio = gray < 0.9 ? Math.round((0.9 - gray) * 100) : 0;
return `color-mix(in srgb, ${ratio}% white, ${color})`;
return changeLightness(r, g, b);
}
#setText(text) {

View File

@ -23,6 +23,7 @@ const {
AnnotationMode,
AnnotationType,
build,
changeLightness,
ColorPicker,
createValidAbsoluteUrl,
DOMSVGFactory,
@ -78,6 +79,7 @@ export {
AnnotationMode,
AnnotationType,
build,
changeLightness,
ColorPicker,
createValidAbsoluteUrl,
DOMSVGFactory,