pebble_image_routines.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. #!/usr/bin/env python
  2. # Copyright 2024 Google LLC
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import math
  16. # This module contains common image and color routines used to convert images
  17. # for use with Pebble.
  18. #
  19. # pebble64 refers to the color palette that is available in color products,
  20. # pebble2 refers to the palette available in b&w products
  21. TRUNCATE = "truncate"
  22. NEAREST = "nearest"
  23. # Create pebble 64 colors-table (r, g, b - 2 bits per channel)
  24. def _get_pebble64_palette():
  25. pebble_palette = []
  26. for i in xrange(0, 64):
  27. pebble_palette.append((
  28. ((i >> 4) & 0x3) * 85, # R
  29. ((i >> 2) & 0x3) * 85, # G
  30. ((i ) & 0x3) * 85)) # B
  31. return pebble_palette
  32. def nearest_color_to_pebble64_palette(r, g, b, a):
  33. """
  34. match each rgba32 pixel to the nearest color in the 8 bit pebble palette
  35. returns closest rgba32 color triplet (r, g, b, a)
  36. """
  37. a = ((a + 42) // 85) * 85 # fast nearest alpha for 2bit color range
  38. # clear transparent pixels (makes image more compress-able)
  39. # and required for greyscale tests
  40. if a == 0:
  41. r, g, b = (0, 0, 0)
  42. else:
  43. r = ((r + 42) // 85) * 85 # nearest for 2bit color range
  44. g = ((g + 42) // 85) * 85 # nearest for 2bit color range
  45. b = ((b + 42) // 85) * 85 # nearest for 2bit color range
  46. return r, g, b, a
  47. def nearest_color_to_pebble2_palette(r, g, b, a):
  48. """
  49. match each rgba32 pixel to the nearest color in 2 bit pebble palette
  50. returns closest rgba32 color triplet (r, g, b, a)
  51. """
  52. # these constants come from ITU-R recommendation BT.709
  53. luma = (r * 0.2126 + g * 0.7152 + b * 0.11)
  54. def round_to_1_bit(value):
  55. """ Round a [0-255] value to either 0 or 255 """
  56. if value > (255 / 2):
  57. return 255
  58. return 0
  59. rounded_luma = round_to_1_bit(luma)
  60. return (rounded_luma, rounded_luma, rounded_luma, round_to_1_bit(a))
  61. def truncate_color_to_pebble64_palette(r, g, b, a):
  62. """
  63. converts each rgba32 pixel to the next lower matching color (truncate method)
  64. in the pebble palette
  65. returns the truncated color as a rgba32 color triplet (r, g, b, a)
  66. """
  67. a = (a // 85) * 85 # truncate alpha for 2bit color range
  68. # clear transparent pixels (makes image more compress-able)
  69. # and required for greyscale tests
  70. if a == 0:
  71. r, g, b = (0, 0, 0)
  72. else:
  73. r = (r // 85) * 85 # truncate for 2bit color range
  74. g = (g // 85) * 85 # truncate for 2bit color range
  75. b = (b // 85) * 85 # truncate for 2bit color range
  76. return r, g, b, a
  77. def truncate_color_to_pebble2_palette(r, g, b, a):
  78. """
  79. converts each rgba32 pixel to the next lower matching color (truncate method)
  80. returns closest rgba32 color triplet (r, g, b, a)
  81. """
  82. if a != 255:
  83. a = 0
  84. if r == 255 and g == 255 and b == 255:
  85. return r, g, b, a
  86. else:
  87. return 0, 0, 0, a
  88. def rgba32_triplet_to_argb8(r, g, b, a):
  89. """
  90. converts a 32-bit RGBA color by channel to an ARGB8 (1 byte containing all 4 channels)
  91. """
  92. a, r, g, b = (a >> 6, r >> 6, g >> 6, b >> 6)
  93. argb8 = (a << 6) | (r << 4) | (g << 2) | b
  94. return argb8
  95. # convert 32-bit color (r, g, b, a) to 32-bit RGBA word
  96. def rgba32_triplet_to_rgba32(r, g, b, a):
  97. return (((r & 0xFF) << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF))
  98. # takes number of colors and outputs PNG & PBI compatible bit depths for paletted images
  99. def num_colors_to_bitdepth(num_colors):
  100. bitdepth = int(math.ceil(math.log(num_colors, 2)))
  101. # only bitdepth 1,2,4 and 8 supported by PBI and PNG
  102. if bitdepth == 0:
  103. # caused when palette has only 1 color
  104. bitdepth = 1
  105. elif bitdepth == 3:
  106. bitdepth = 4
  107. elif bitdepth > 4:
  108. bitdepth = 8
  109. return bitdepth
  110. def get_reduction_func(palette_name, color_reduction_method):
  111. reduction_funcs = {
  112. 'pebble64': {
  113. NEAREST: nearest_color_to_pebble64_palette,
  114. TRUNCATE: truncate_color_to_pebble64_palette
  115. },
  116. 'pebble2': {
  117. NEAREST: nearest_color_to_pebble2_palette,
  118. TRUNCATE: truncate_color_to_pebble2_palette
  119. }
  120. }
  121. return reduction_funcs[palette_name][color_reduction_method]