Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ deltachat_derive = { path = "./deltachat_derive" }
libc = "0.2.51"
pgp = { version = "0.7.0", default-features = false }
hex = "0.4.0"
sha-1 = "0.9.3"
sha2 = "0.9.0"
rand = "0.7.0"
smallvec = "1.0.0"
Expand Down Expand Up @@ -64,6 +65,7 @@ url = "2.1.1"
async-std-resolver = "0.19.5"
async-tar = "0.3.0"
uuid = { version = "0.8", features = ["serde", "v4"] }
rust-hsluv = "0.1.4"

pretty_env_logger = { version = "0.4.0", optional = true }
log = {version = "0.4.8", optional = true }
Expand Down
9 changes: 5 additions & 4 deletions src/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use serde::{Deserialize, Serialize};
use crate::aheader::EncryptPreference;
use crate::blob::{BlobError, BlobObject};
use crate::chatlist::dc_get_archived_cnt;
use crate::color::str_to_color;
use crate::config::Config;
use crate::constants::{
Blocked, Chattype, ShowEmails, Viewtype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK,
Expand All @@ -26,8 +27,8 @@ use crate::contact::{addr_cmp, Contact, Origin, VerifiedStatus};
use crate::context::Context;
use crate::dc_tools::{
dc_create_id, dc_create_outgoing_rfc724_mid, dc_create_smeared_timestamp,
dc_create_smeared_timestamps, dc_get_abs_path, dc_gm2local_offset, dc_str_to_color,
improve_single_line_input, time, IsNoneOrEmpty,
dc_create_smeared_timestamps, dc_get_abs_path, dc_gm2local_offset, improve_single_line_input,
time, IsNoneOrEmpty,
};
use crate::ephemeral::{delete_expired_messages, schedule_ephemeral_task, Timer as EphemeralTimer};
use crate::events::EventType;
Expand Down Expand Up @@ -891,7 +892,7 @@ impl Chat {
}
}
} else {
color = dc_str_to_color(&self.name);
color = str_to_color(&self.name);
}

color
Expand Down Expand Up @@ -3081,7 +3082,7 @@ mod tests {
"param": "",
"gossiped_timestamp": 0,
"is_sending_locations": false,
"color": 15895624,
"color": 35391,
"profile_image": "",
"draft": "",
"is_muted": false,
Expand Down
46 changes: 46 additions & 0 deletions src/color.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//! Implementation of Consistent Color Generation
//!
//! Consistent Color Generation is defined in XEP-0392.
//!
//! Color Vision Deficiency correction is not implemented as Delta Chat does not offer
//! corresponding settings.
use hsluv::hsluv_to_rgb;
use sha1::{Digest, Sha1};

/// Converts an identifier to Hue angle.
fn str_to_angle(s: impl AsRef<str>) -> f64 {
let bytes = s.as_ref().as_bytes();
let result = Sha1::digest(bytes);
let checksum: u16 = result.get(0).map_or(0, |&x| u16::from(x))
+ 256 * result.get(1).map_or(0, |&x| u16::from(x));
f64::from(checksum) / 65536.0 * 360.0
}

/// Converts an identifier to RGB color.
///
/// Returns a 24-bit number with 8 least significant bits corresponding to the blue color and 8
/// most significant bits corresponding to the red color.
///
/// Saturation is set to maximum (100.0) to make colors distinguishable, and lightness is set to
/// half (50.0) to make colors suitable both for light and dark theme.
pub(crate) fn str_to_color(s: impl AsRef<str>) -> u32 {
let (r, g, b) = hsluv_to_rgb((str_to_angle(s), 100.0, 50.0));
65536 * (r * 256.0) as u32 + 256 * (g * 256.0) as u32 + (b * 256.0) as u32
}

#[cfg(test)]
mod tests {
use super::*;

#[allow(clippy::float_cmp)]
#[test]
fn test_str_to_angle() {
// Test against test vectors from
// https://xmpp.org/extensions/xep-0392.html#testvectors-fullrange-no-cvd
assert!((str_to_angle("Romeo") - 327.255249).abs() < 1e-6);
assert!((str_to_angle("juliet@capulet.lit") - 209.410400).abs() < 1e-6);
assert!((str_to_angle("😺") - 331.199341).abs() < 1e-6);
assert!((str_to_angle("council") - 359.994507).abs() < 1e-6);
assert!((str_to_angle("Board") - 171.430664).abs() < 1e-6);
}
}
7 changes: 3 additions & 4 deletions src/contact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ use regex::Regex;

use crate::aheader::EncryptPreference;
use crate::chat::ChatId;
use crate::color::str_to_color;
use crate::config::Config;
use crate::constants::{
Chattype, DC_CHAT_ID_DEADDROP, DC_CONTACT_ID_DEVICE, DC_CONTACT_ID_DEVICE_ADDR,
DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF, DC_GCL_ADD_SELF, DC_GCL_VERIFIED_ONLY,
};
use crate::context::Context;
use crate::dc_tools::{
dc_get_abs_path, dc_str_to_color, improve_single_line_input, listflags_has, EmailAddress,
};
use crate::dc_tools::{dc_get_abs_path, improve_single_line_input, listflags_has, EmailAddress};
use crate::events::EventType;
use crate::key::{DcKey, SignedPublicKey};
use crate::login_param::LoginParam;
Expand Down Expand Up @@ -947,7 +946,7 @@ impl Contact {
/// and can be used for an fallback avatar with white initials
/// as well as for headlines in bubbles of group chats.
pub fn get_color(&self) -> u32 {
dc_str_to_color(&self.addr)
str_to_color(&self.addr)
}

/// Gets the contact's status.
Expand Down
25 changes: 0 additions & 25 deletions src/dc_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,31 +48,6 @@ pub(crate) fn dc_truncate(buf: &str, approx_chars: usize) -> Cow<str> {
}
}

/// the colors must fulfill some criterions as:
/// - contrast to black and to white
/// - work as a text-color
/// - being noticeable on a typical map
/// - harmonize together while being different enough
/// (therefore, we cannot just use random rgb colors :)
const COLORS: [u32; 16] = [
0xe5_65_55, 0xf2_8c_48, 0x8e_85_ee, 0x76_c8_4d, 0x5b_b6_cc, 0x54_9c_dd, 0xd2_5c_99, 0xb3_78_00,
0xf2_30_30, 0x39_b2_49, 0xbb_24_3b, 0x96_40_78, 0x66_87_4f, 0x30_8a_b9, 0x12_7e_d0, 0xbe_45_0c,
];

#[allow(clippy::indexing_slicing)]
pub(crate) fn dc_str_to_color(s: impl AsRef<str>) -> u32 {
let str_lower = s.as_ref().to_lowercase();
let mut checksum = 0;
let bytes = str_lower.as_bytes();
for (i, byte) in bytes.iter().enumerate() {
checksum += (i + 1) * *byte as usize;
checksum %= 0x00ff_ffff;
}
let color_index = checksum % COLORS.len();

COLORS[color_index]
}

/* ******************************************************************************
* date/time tools
******************************************************************************/
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ pub mod stock_str;
mod token;
#[macro_use]
mod dehtml;
mod color;
pub mod html;
pub mod plaintext;

Expand Down