Mã màu và "struct màu"



  • Mã màu

    Ông bà ta thường nói: "Trên đời này, cái gì cũng có số"! Đúng vậy, đến cả màu sắc còn có số thì huống chi con người, đồ vật... 😂

    Trong lập trình, màu sắc được kết hợp giữa ba thành phần màu cơ bản và thứ tự gồm đỏ, lụcxanh. Trong các công nghệ rendering tiên tiến hơn sẽ có thêm thành phần alpha (opacity) chỉ độ trong suốt của màu sắc được hiển thị.

    Theo quy chuẩn chung thì các thành phần này sẽ dao động từ 0 (yếu nhất) đến 255 (mạnh nhất) và được biểu diễn dưới dạng hex.

    Hex color: AARRGGBB (ARGB)
    Ex:  AABBCCDD -> num: 2864434397
         \_\_\_\_
          | | | +----> blue:    221
          | | +------> green:   204
          | +--------> red:     187
          +----------> alpha: 170
    

    Để tách các thành phần từ mã màu, ta chỉ cần sử dụng các toán tử bit.

    alpha = (ARGB >> 24) & 0xff
    red   = (ARGB >> 16) & 0xff
    green = (ARGB >>  8) & 0xff
    blue  = ARGB & 0xff
    
    • Sử dụng phép và để giới hạn giá trị

    Xét theo một cấp độ thấp hơn, ta sẽ thấy rằng các thành phần có giá trị tối đa là 0xff (255) tương đương với 8bit (kiểu byte hoặc uint8). Vì vậy trong Go, mã màu thường được sử dụng với kiểu uint (hoặc uint32) là tối ưu nhất. Kiểu này sử dụng 32bit và chứa trọn các thành phần bên trong (32 = 8 x 4).

    Quay trở lại công thức mã màu ở trên, ta sẽ thấy các thành phần cách nhau đến 8bit (1byte) và chúng sắp xếp theo một trật tự nhất định. Có bao giờ bạn nghĩ đến chuyện không sử dụng các phép toán, các toán tử bit mà vẫn tính toán, tạo lập được mã màu? 😂

    Xây dựng struct màu

    Có một điều thật thú vị đó là Go có hỗ trợ struct theo quy chuẩn của C. Các thành phần trong struct được sắp xếp theo một trật tự nhất định và phân theo từng bit một. Vì vậy ta hoàn toàn có thể xây dựng một cấu trúc mã màu bằng cách sử dụng struct.

    Đầu tiên là mã màu có 4 thành phần alpha, red, greenblue.

    type Color struct {
        alpha
        red
        greencolored text
        blue
    }
    

    Các thành phần đều thuộc kiểu byte (hoặc uint8)

        alpha    byte
        red      byte
        green    byte
        blue     byte
    

    Các thành phần theo một thứ tự bit nhất định, thấp nhất là blue, đến greenred, cao nhất là alpha. Chỉ cần đảo ngược thứ tự của chúng.

        blue     byte
        green    byte
        red      byte
        alpha    byte
    

    Đến đây sẽ có một câu hỏi đặt ra:

    • Vậy làm sao để chuyển nó về mã màu? _
    • Trong khi struct không thể cast trực tiếp sang số nguyên, chả lẽ lấy các member kết hợp với nhau bằng toán tử bit? _
    • Mày đùa à?

    Để cast struct trên sang số nguyên, mình sẽ sử dụng đến con trỏ (pointer). Do kiểu interface{} yêu cầu phải sử dụng assertion để check lỗi, khá là khó chịu nên mình sử dụng type unsafe.Pointer.

    Đầu tiên là tạo một màu bằng struct Color

        var a Color = Color{0xDD, 0xCC, 0xBB, 0xAA} // AABBCCDD
        // hoặc như này cho dễ nhìn
        a.alpha = 0xAA
        a.red   = 0xBB
        a.green = 0xCC
        a.blue  = 0xDD
    

    Tiếp đến lấy pointer của nó và cast về kiểu pointer uint32 và lấy giá trị từ pointer đó:

        var b *uint32 = (*uint32)(unsafe.Pointer(&a))
        fmt.Printf("%08X\n", *b)
        // in ra AABBCCDD
    

    Việc đổi ngược lại cũng không có gì khó, viết luôn thành hàm sẽ tiện hơn.

    func _toARGB(pclr *Color) uint32 {
        return *(*uint32)(unsafe.Pointer(pclr))
    }
    
    func _toColor(pargb *uint32) *Color {
        return (*Color)(unsafe.Pointer(pargb))
    }
    

    Xong rồi, bạn có thể test code trên tại đây: https://play.golang.org/p/won7uXrFn4d

    Có lẽ tính toán mã màu theo phong cách này rất là cool ngầu 😂 vừa không cần tính toán nhiều mà lại còn dễ nhìn, dễ thay đổi giá trị hơn nữa đấy. Còn về hiệu năng thì theo mình cũng không khác là mấy, sở dĩ việc align các thành phần trong struct cũng nhờ đến các bitwise mà.

    Notes

    • assertion ở đây có thể do Go không chấp nhận các pointer trỏ đến null, vì vậy phải check
    • ngoài sử dụng cho mã màu, phương pháp này còn có thể ứng dụng trong nhiều loại số khác nhau, chẳng hạn flag bit
    • có thể up to số nguyên 64bit với uint64 (format 0xfffffffffffffffff) và struct {bytex8} hoặc {uint16x4} hay {uintx2} 😂

  • Trùm cuối

    Bài chất quá bác 😂


Hãy đăng nhập để trả lời
 

Có thể bạn cũng quan tâm

.
DMCA.com Protection Status