Refactor
parent
de6364fef2
commit
7a8f314be1
|
@ -73,16 +73,16 @@ function show_edit_colors_window($swatch_to_edit, color_selection_slot_to_edit)
|
|||
let old_rgba = get_rgba_from_color(palette[swatch_index]);
|
||||
const new_rgba = get_rgba_from_color(color);
|
||||
const other_rgba = get_rgba_from_color(palette[14 - swatch_index]);
|
||||
const was_monochrome = detect_monochrome(ctx);
|
||||
const was_monochrome_selection = (selection && selection.canvas) ? detect_monochrome(selection.canvas.ctx) : was_monochrome;
|
||||
const main_monochrome_info = detect_monochrome(ctx);
|
||||
const selection_monochrome_info = (selection && selection.canvas) ? detect_monochrome(selection.canvas.ctx) : main_monochrome_info;
|
||||
const selection_matches_main_canvas_colors =
|
||||
was_monochrome_selection &&
|
||||
was_monochrome_selection.every((rgba)=>
|
||||
was_monochrome.map(rgba=> rgba.toString()).includes(rgba.toString())
|
||||
selection_monochrome_info.isMonochrome &&
|
||||
selection_monochrome_info.presentNonTransparentRGBAs.every((rgba)=>
|
||||
main_monochrome_info.presentNonTransparentRGBAs.map(rgba=> rgba.toString()).includes(rgba.toString())
|
||||
);
|
||||
if (
|
||||
was_monochrome &&
|
||||
was_monochrome_selection &&
|
||||
main_monochrome_info.isMonochrome &&
|
||||
selection_monochrome_info.isMonochrome &&
|
||||
selection_matches_main_canvas_colors
|
||||
) {
|
||||
const recolor = (ctx, present_rgbas)=> {
|
||||
|
@ -112,9 +112,9 @@ function show_edit_colors_window($swatch_to_edit, color_selection_slot_to_edit)
|
|||
name: "Recolor",
|
||||
icon: get_help_folder_icon("p_monochrome_undo.png"),
|
||||
}, ()=> {
|
||||
recolor(ctx, was_monochrome);
|
||||
recolor(ctx, main_monochrome_info.presentNonTransparentRGBAs);
|
||||
if (selection && selection.canvas) {
|
||||
recolor(selection.canvas.ctx, was_monochrome_selection);
|
||||
recolor(selection.canvas.ctx, selection_monochrome_info.presentNonTransparentRGBAs);
|
||||
// I feel like this shouldn't be necessary, if I'm not changing the size, but it makes it work:
|
||||
selection.replace_source_canvas(selection.canvas);
|
||||
}
|
||||
|
|
|
@ -1907,8 +1907,9 @@ function image_invert_colors(){
|
|||
name: localize("Invert Colors"),
|
||||
icon: get_help_folder_icon("p_invert.png"),
|
||||
}, (original_canvas, original_ctx, new_canvas, new_ctx) => {
|
||||
if (monochrome && detect_monochrome(original_ctx)) {
|
||||
invert_monochrome(original_ctx, new_ctx);
|
||||
const monochrome_info = monochrome && detect_monochrome(original_ctx);
|
||||
if (monochrome && monochrome_info.isMonochrome) {
|
||||
invert_monochrome(original_ctx, new_ctx, monochrome_info);
|
||||
} else {
|
||||
invert_rgb(original_ctx, new_ctx);
|
||||
}
|
||||
|
@ -2024,19 +2025,29 @@ function detect_monochrome(ctx) {
|
|||
// Note: values in pixelArray may be different on big endian vs little endian machines.
|
||||
// Use id.data, which is guaranteed to be in RGBA order, for getting color information.
|
||||
// Only use the Uint32Array for comparing pixel equality (faster than comparing each color component).
|
||||
const colorValues = [];
|
||||
const colorUint32s = [];
|
||||
const colorRGBAs = [];
|
||||
let anyTransparency = false;
|
||||
for(let i=0, len=pixelArray.length; i<len; i+=1){
|
||||
if (!colorValues.includes(pixelArray[i]) && id.data[i*4+3] !== 0) {
|
||||
if (colorValues.length < 2) {
|
||||
colorValues.push(pixelArray[i]);
|
||||
colorRGBAs.push(id.data.slice(i*4, (i+1)*4));
|
||||
} else {
|
||||
return false;
|
||||
if (id.data[i*4+3]) {
|
||||
if (!colorUint32s.includes(pixelArray[i])) {
|
||||
if (colorUint32s.length < 2) {
|
||||
colorUint32s.push(pixelArray[i]);
|
||||
colorRGBAs.push(id.data.slice(i*4, (i+1)*4));
|
||||
} else {
|
||||
return {isMonochrome: false};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
anyTransparency = true;
|
||||
}
|
||||
}
|
||||
return colorRGBAs;
|
||||
return {
|
||||
isMonochrome: true,
|
||||
presentNonTransparentRGBAs: colorRGBAs,
|
||||
presentNonTransparentUint32s: colorUint32s,
|
||||
monochromeWithTransparency: anyTransparency,
|
||||
};
|
||||
}
|
||||
|
||||
function make_monochrome_pattern(lightness, rgba1=[0, 0, 0, 255], rgba2=[255, 255, 255, 255]){
|
||||
|
@ -2269,7 +2280,7 @@ function image_attributes(){
|
|||
const unit = $units.find(":checked").val();
|
||||
|
||||
const was_monochrome = monochrome;
|
||||
const image_was_actually_monochrome = detect_monochrome(ctx);
|
||||
const monochrome_info = detect_monochrome(ctx);
|
||||
|
||||
image_attributes.unit = unit;
|
||||
transparency = (transparency_option == "transparent");
|
||||
|
@ -2277,8 +2288,8 @@ function image_attributes(){
|
|||
|
||||
if(monochrome != was_monochrome){
|
||||
if(monochrome){
|
||||
if(image_was_actually_monochrome && image_was_actually_monochrome.length === 2) {
|
||||
palette = make_monochrome_palette(image_was_actually_monochrome[0], image_was_actually_monochrome[1]);
|
||||
if(monochrome_info.isMonochrome && monochrome_info.presentNonTransparentRGBAs.length === 2) {
|
||||
palette = make_monochrome_palette(...monochrome_info.presentNonTransparentRGBAs);
|
||||
}else{
|
||||
palette = monochrome_palette;
|
||||
}
|
||||
|
@ -2307,7 +2318,7 @@ function image_attributes(){
|
|||
// We only want to show this dialog if it would also change the palette (above), never leave you on an outdated palette.
|
||||
// (And it's nice to be able to change other options without worrying about it trying to convert the document to monochrome.)
|
||||
if(monochrome != was_monochrome){
|
||||
if (monochrome && !image_was_actually_monochrome) {
|
||||
if (monochrome && !monochrome_info.isMonochrome) {
|
||||
show_convert_to_black_and_white();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -748,38 +748,18 @@ function invert_rgb(source_ctx, dest_ctx=source_ctx) {
|
|||
dest_ctx.putImageData(image_data, 0, 0);
|
||||
}
|
||||
|
||||
function invert_monochrome(source_ctx, dest_ctx=source_ctx) {
|
||||
function invert_monochrome(source_ctx, dest_ctx=source_ctx, monochrome_info=detect_monochrome(source_ctx)) {
|
||||
const image_data = source_ctx.getImageData(0, 0, source_ctx.canvas.width, source_ctx.canvas.height);
|
||||
// Note: values in pixel_array may be different on big endian vs little endian machines.
|
||||
// Only rely on equality of values within the array.
|
||||
// pixel_array is a performance optimization, to access whole pixels at a time instead of individual color channels.
|
||||
// Note: code copied from detect_monochrome; @TODO: make detect_monochrome return an object with various info
|
||||
const pixel_array = new Uint32Array(image_data.data.buffer);
|
||||
const color_values = [];
|
||||
const color_rgbas = [];
|
||||
let any_transparency = false;
|
||||
for(let i=0, len=pixel_array.length; i<len; i+=1){
|
||||
if (image_data.data[i*4+3]) {
|
||||
if (!color_values.includes(pixel_array[i])) {
|
||||
if (color_values.length < 2) {
|
||||
color_values.push(pixel_array[i]);
|
||||
color_rgbas.push(image_data.data.slice(i*4, (i+1)*4));
|
||||
} else {
|
||||
console.error("Not monochrome!");
|
||||
dest_ctx.putImageData(image_data, 0, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
any_transparency = true;
|
||||
}
|
||||
}
|
||||
if (color_values.length === 0) {
|
||||
if (monochrome_info.presentNonTransparentUint32s.length === 0) {
|
||||
// Fully transparent.
|
||||
// No change, and no need to copy the image to dest canvas to represent that lack of a change.
|
||||
return;
|
||||
}
|
||||
if (color_values.length === 1) {
|
||||
if (monochrome_info.presentNonTransparentUint32s.length === 1) {
|
||||
// Only one non-transparent color present in the image.
|
||||
// Can't use just the information of what colors are in the canvas to invert, need to look at the palette.
|
||||
// We could've done this in a unified way, but whatever!
|
||||
|
@ -788,28 +768,30 @@ function invert_monochrome(source_ctx, dest_ctx=source_ctx) {
|
|||
const color_1 = palette[0];
|
||||
const color_2 = palette[14] || palette[1];
|
||||
const color_1_rgba = get_rgba_from_color(color_1);
|
||||
const present_rgba = monochrome_info.presentNonTransparentRGBAs[0];
|
||||
if (
|
||||
color_rgbas[0][0] === color_1_rgba[0] &&
|
||||
color_rgbas[0][1] === color_1_rgba[1] &&
|
||||
color_rgbas[0][2] === color_1_rgba[2] &&
|
||||
color_rgbas[0][3] === color_1_rgba[3]
|
||||
present_rgba[0] === color_1_rgba[0] &&
|
||||
present_rgba[1] === color_1_rgba[1] &&
|
||||
present_rgba[2] === color_1_rgba[2] &&
|
||||
present_rgba[3] === color_1_rgba[3]
|
||||
) {
|
||||
dest_ctx.fillStyle = color_2;
|
||||
} else {
|
||||
dest_ctx.fillStyle = color_1;
|
||||
}
|
||||
if (any_transparency) {
|
||||
if (monochrome_info.any_transparency) {
|
||||
dest_ctx.putImageData(image_data, 0, 0);
|
||||
dest_ctx.globalCompositeOperation = "source-in";
|
||||
}
|
||||
dest_ctx.fillRect(0, 0, source_ctx.canvas.width, source_ctx.canvas.height);
|
||||
return;
|
||||
}
|
||||
const [uint32_a, uint32_b] = monochrome_info.presentNonTransparentUint32s;
|
||||
for(let i=0, len=pixel_array.length; i<len; i+=1){
|
||||
if (pixel_array[i] === color_values[0]) {
|
||||
pixel_array[i] = color_values[1];
|
||||
} else if (pixel_array[i] === color_values[1]) {
|
||||
pixel_array[i] = color_values[0];
|
||||
if (pixel_array[i] === uint32_a) {
|
||||
pixel_array[i] = uint32_b;
|
||||
} else if (pixel_array[i] === uint32_b) {
|
||||
pixel_array[i] = uint32_a;
|
||||
}
|
||||
}
|
||||
dest_ctx.putImageData(image_data, 0, 0);
|
||||
|
|
Loading…
Reference in New Issue