เอ๊ะ ในหน้าเว็บ material.io เขาคำนวณยังไงว่าสีนี้ต้องใช้ตัวหนังสือสีขาวหรือสีดำ

Minseo Chayabanjonglerd
Black Lens
Published in
4 min readFeb 11, 2019

ถ้าในแอปสามารถทำได้เหมือนกัน ก็คงจะดีสินะ…

เมื่อเราจิ้มสีในหน้าเว็บ https://material.io/tools/color/#!/?view.left=0&view.right=0 มันจะ generate มาให้เลยว่า สีนี้อ่ะ ใช้ text สีขาวนะ ส่วนอันนี้ใช้ text สีดำ

ช่วงนี้เราสนใจเรื่อง Material Design เป็นพิเศษ (ปกติสนใจอยู่แล้ว) และถ้าเราทำแอปนึงขึ้นมานั้น มันจะชอบให้สีซึ่งเราเองไม่ค่อยชอบเท่าไหร่นัก ขึ้นมาให้เราด้วย

จริงๆในหน้าเว็บเมื่อกี้นี้ เราสามารถจิ้มพวกสีเพื่อเอามาใช้เปลี่ยนในแอปได้ด้วย สำหรับการ set สีสำคัญในแอป 3 สี อันได้แก่ colorPrimary, colorPrimaryDark และ colorAccent แน่นอนว่า การทำแอปของแต่ละ brand ต่างๆ เรื่องสีพวกนี้ก็มาจาก brandCI นั่นเอง รวมถึงสีตัวหนังสือทาง UI Designer ก็ใส่ให้เสร็จเลยแหละเนอะ

ถ้าเราจิ้มสี colorPrimary และ colorPrimaryDark เล่นไปเรื่อยๆ จะพบว่า ตัวหนังสือที่แสดงในช่อง CURRENT SCHEME นั้น จะมีแค่สีขาวกับสีดำเท่านั้น (จริงๆจะลองใส่สีอื่นก็ได้)

เมื่อกดสลับแท็ปมาที่ ACCESSIBILITY ก็พบความชัดเจนมากขึ้น มันแสดงให้เราเห็นเลยว่า เห้ยยย พื้นหลังสีนี้อ่ะ ใช้สีขาวได้นะ สีดำอ่านไม่เห็น เออสีนี้อ่ะ สีขาวอ่านได้ สีดำอาจจะไม่ค่อยเห็นนะ ซึ่งมันขึ้นอยู่กับขนาดของตัวหนังสือด้วยแหละ

เอ้ออออ แล้วมันรู้ได้ยังไงว่า ทำไมสีนี้ถึงบอกว่าตัวหนังสือสีขาวดีกว่า หรือสีนี้ทำไมสีดำดีกว่า ทั้งๆที่บางทีสีขาวก็ได้นี่นา

เมื่อกดไปดู LEARN MORE เลยพบว่า มันใช้วิธีคำนวณหาค่า Contrast ระหว่างสีของพื้นหลัง และสีของตัวหนังสือ และใช้ ratio อยู่ที่ 4.5:1 สำหรับตัวหนังสือปกติ และ 3:1 สำหรับตัวหนังสือตัวใหญ่ เป็นมาตรฐานของ W3 ที่ website ต่างๆใช้เป็น guideline กัน

Luminance คืออะไรกันนะ?

ก่อนดูเรื่องการคำนวณเรามารู้จักเจ้า luminance กันก่อน ซึ่ง luminance ในทาง Image Processing คือความเข้มของแสง ซึ่งใน Android สามารถใช้เรียกได้แบบนี้

Color.luminance(0xFEFEFE)

แต่ว่า มันเริ่มมีตอน API Level 24 อ่ะ ดังนั้นถ้าคนอ่านเอาไปใช้ อย่าลืมดัก version ด้วยนะเออ เดี๋ยวใช้แล้วแอปพัง

ดังนั้นลองกดดูรายละเอียดการคำนวณสิ ว่าเป็นอย่างไร

เข้าใจว่าน่าจะเป็นทฤษฏีหนึ่งที่น่าจะเคยเรียนมาในวิชา Image Processing แต่จำไม่ได้ง่ะว่าเป็นของใคร อะไรยังไง https://www.w3.org/TR/WCAG20-TECHS/G18.html

จับใจความได้ว่า หาเจ้า luninance ของสีพื้นหลังเรามาก่อน แล้วมาคำนวณหา contrast ratio

  • สูตรการคำนวณหาเจ้า luninance คือ
L = (0.2126 * R) + (0.7152 * G) + (0.0722 * B)
  • สีมันจะมี Red Green Blue มาประกอบร่างกัน โดยเรามองว่าสีมี 3 ก้อนใหญ่ๆแล้วกัน แต่ละก้อนทั้ง R G และ B มีการคำนวณที่เหมือนกัน คือ
R = when (R8bit/255 <= 0.03928) {
true -> (R8bit/255)/12.92
false -> (((R8bit/255) + 0.055)/1.055) ^ 2.4
}
G = when (G8bit/255 <= 0.03928) {
true -> (G8bit/255)/12.92
false -> (((G8bit/255) + 0.055)/1.055) ^ 2.4
}
B = when (B8bit/255 <= 0.03928) {
true -> (B8bit/255)/12.92
false -> (((B8bit/255) + 0.055)/1.055) ^ 2.4
}
  • จากนั้นเข้าสูตรการคำนวณ ได้ luninance มา
  • นำเจ้า luninance ที่ได้น้านนนน ไปคำนวณหา Contrast Ratio ต่อจ้า ซึ่งอันนี้แหละ จุดตาย คืออันไหนที่ถูกต้อง

Contrast Ratio แบบไหน ที่หาตัวหนังสือสีขาวดำให้เรา?

อันนี้แหละ ที่ทำเรางง ว่าเปรียบเทียบหาแบบไหนถึงจะถูกต้อง เราเลยมาลองใน Numbers ดูว่า ผลการคำนวณเป็นอย่างไร เราจะใช้สีที่เป็นกึ่งกลางผลขาวดำ นำมาเป็น test case ว่าถูกต้องไหม

เช่น สีแดง Red500 ใช้ตัวหนังสือสีดำ และ Red600 ใช้ตัวหนังสือสีขาว มันเหมือนมีเส้นกั้นแดนอ่ะ

สูตรการหา Contrast Ratio แบบดั้งเดิม

return when ((L + 0.05) / (L2 + 0.05) > (L1 + 0.05) / (L + 0.05)) {
true -> #000000 //black
false -> #FFFFFF //white
}
  • L1 ก็คือ สีที่สว่างกว่า (lighter) ถ้าเป็นสีขาวนั้น ค่า L1 = 1.0
  • L2 คือ สีที่มืดกว่า (darker) ถ้าเป็นสีดำ ค่า L2 = 0.0

ดังนั้น Contrast Ratio ก็คือ

(L + 0.05) / (0.0 + 0.05) > (1.0 + 0.05) / (L + 0.05)

วิธีแรก แทนค่าจากสูตรไปตรงๆ

return when ((L + 0.05) / (0.05) > (1.05) / (L + 0.05)) {
true -> #000000 //black
false -> #FFFFFF //white
}

ซึ่งใน stackoverflow ก็มีคนเขียนโค้ดให้ดูแล้วแหละ

ผลการคำนวณใน Numbers จะเป็นดังนี้

จากการลองจิ้มดูพบว่า ถ้าใช้พื้นหลังสีเหลือง ตัวหนังสือต้องเป็นสีดำเท่านั้น เลยหยิบแบบสุดๆมา

วิธีที่สอง ในเมื่อตัวหนังสือปกติมี Contrast Ratio ที่ 4.5:1 ดังนั้นถ้าเอาไปเข้าสูตรนี้แล้ว ถ้าอันไหนมากกว่าหรือเท่ากับ 4.5 ก็อันนั้นแหละ

(L1 + 0.05) / (L2 + 0.05)

งงหล่ะสิ เออคนเขียนก็งง เอาแบบนี้แล้วกัน มองเป็นแบบนี้

(black #000000) — — (Indigo300 #7986cb) — — - — — (white #FFFFFF)

สีที่เราเอาไปหา luninance มันจะอยู่ตรงกลางขาวและดำเนอะ ดังนั้นคู่ที่เราหา Contrast Ratio นั้น คือ

  • คู่สีดำกับ Indigo300 โดย L1 สีสว่างคือ Indigo300 และ L2 สีมืดคือสีดำ จะได้เป็น
(L + 0.05) / (0.0 + 0.05)
  • คู่ Indigo300 กับสีขาว โดย L1 คือสีขาว และ L2 คือ Indigo300 จะได้
(1.0 + 0.05) / (L + 0.05)

ถ้าอันไหนมากกว่า 4.5 ก็จะเป็นสีนั้นนั่นเอง

จากการคำนวณใน Numbers สำหรับวิธีที่สอง ผลจะออกมาดังนี้

ซึ่งทั้งสองวิธีนี้จะได้ผลที่เหมือนกันเลย เย้อิเยอิเย่~~

แต่กับสี Lime900 นั้น ผลมันไม่ตรงกับในเว็บนะสำหรับวิธีแรก แต่วิธีที่สองเราเทียบสีขาวก่อน ผลเลยถูก (ค่ามากกว่า 4.5 ทั้งคู่) ซึ่งไม่แน่ใจว่ามีกี่สีที่เราต้องระวังอะนะ

ซึ่งเรามาลองเขียนให้เป็น library ตัวนึง จากการศึกษามาคร่าวๆจากด้านบน เจอปัญหาเรื่องการหาค่า luninance ที่ไม่ตรงกับที่เราคำนวณเอง ;_;

ต้องระวังนิดนึงเรื่อง type ของเลขต่างๆเนอะ เวลามันเป็น Int / Int มันได้ Int มันไม่เหมือนเครื่องคิดเลขนะเออ ไม่มีเศษทศนิยมมาให้เรา ดังนั้นทศนิยม ควรคิดเป็น Double

จากนั้นที่เหลือก็เรียบโร้ยแล้วจ้า ผลออกมาก็ค่อนข้างตรง มีบางสีที่อาจจะได้ตัวหนังสือไม่ตรงกับสีในเว็บ Material เนอะ เอาเป็นว่าสามารถเข้าไปส่องใน github และ pull request ได้จ้า ช่วยเรา Contribute ด้วยยยยย~~~

ตัว Library นี้เราอัพลง JitPack ซึ่งการเรียก Library มาใช้งานก็ไม่น่าจะยากอะไร

ส่วนจะเริ่มทำ library อย่างไร ติดตามชมตอนต่อๆไปจ้า

เชื่อว่าเริ่มจะมีคำถาม “เอ่อออพี่เจ้าของบล็อกครับ ถ้าอยากจะคำนวณสีตัวหนังสือสีอื่นจะได้ไหมครับ?”

หาเจ้า luninance ของสีที่เราต้องการทั้งพื้นหลังและสีตัวหนังสือ แล้วเอาไปเข้าสูตรหา Contrast Ratio ตามวิธีที่สองจ้า หรือไม่ก็วิธีแรก แต่เปรียบเทียบกับขาวดำร่วมด้วยจ้า

สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ

--

--

Minseo Chayabanjonglerd
Black Lens

Android Developer | Content Creator AKA. MikkiPastel | Web2 & Web3 Contributor