Skip to content

Commit 7f5edb5

Browse files
authored
update: 3.0.0 (#24)
update: `3.0.0`
2 parents 36bd0f4 + 8d24775 commit 7f5edb5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+4558
-2085
lines changed

.github/workflows/cibuildwheel.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Build and Test Wheels
2+
3+
on:
4+
pull_request:
5+
push:
6+
tags:
7+
- 'v*'
8+
9+
jobs:
10+
build_wheels:
11+
name: Build wheels on ${{ matrix.os }}
12+
runs-on: ${{ matrix.os }}
13+
strategy:
14+
matrix:
15+
os: [ubuntu-latest, macos-latest, windows-latest]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
submodules: recursive
21+
22+
- name: Install Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: '3.x' # cibuildwheel will use its own Python versions
26+
27+
- name: Install cibuildwheel
28+
run: python -m pip install cibuildwheel
29+
30+
- name: Build wheels
31+
run: python -m cibuildwheel --output-dir wheelhouse
32+
env:
33+
CIBW_BUILD_VERBOSITY: 1
34+
CIBW_SKIP: "cp37-* cp38-* pp*" # Skip Python 3.7, 3.8 and PyPy as per existing workflows
35+
CIBW_ARCHS_MACOS: "x86_64 arm64" # Build for both architectures on macOS
36+
CIBW_BEFORE_TEST: >
37+
pip install -r {project}/requirements.txt -r {project}/requirements-dev.txt
38+
CIBW_TEST_COMMAND: >
39+
curl -L -o test_image.jpg "https://raw.githubusercontent.com/mohammadimtiazz/standard-test-images-for-Image-Processing/refs/heads/master/standard_test_images/tulips.png" &&
40+
python {project}/tests/test_all.py --image test_image.jpg --quality 1 --method pillow --tonal-spot &&
41+
python {project}/tests/test_all.py --image test_image.jpg --quality 1 --method cpp --tonal-spot
42+
CIBW_BEFORE_BUILD: >
43+
pip install setuptools wheel
44+
CIBW_ENVIRONMENT: >
45+
PURE_PYTHON=False
46+
47+
- uses: actions/upload-artifact@v4
48+
with:
49+
name: cibuildwheel-wheels-${{ matrix.os }}
50+
path: wheelhouse/*.whl
51+
52+
deploy:
53+
name: Publish to PyPI
54+
needs: build_wheels
55+
runs-on: ubuntu-latest
56+
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
57+
58+
steps:
59+
- uses: actions/checkout@v4
60+
61+
- name: Download all wheels
62+
uses: actions/download-artifact@v4
63+
with:
64+
path: dist
65+
66+
- name: Publish to PyPI
67+
uses: pypa/gh-action-pypi-publish@release/v1
68+
with:
69+
user: __token__
70+
password: ${{ secrets.PYPI_API_TOKEN }}
71+
packages_dir: dist

.github/workflows/default.yml

Lines changed: 0 additions & 67 deletions
This file was deleted.

.github/workflows/linux.yml

Lines changed: 0 additions & 48 deletions
This file was deleted.

.setup.py.kate-swp

119 Bytes
Binary file not shown.

README.md

Lines changed: 90 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
![image](https://github.com/T-Dynamos/materialyoucolor-pyhton/assets/68729523/b29c17d1-6c02-4c07-9a72-5b0198034760)
2-
31
# [Material You color algorithms](https://m3.material.io/styles/color/overview) for python!
42
It is built in reference with offical [typescript implementation](https://github.com/material-foundation/material-color-utilities/tree/main/typescript) except it's color quantization part, which is based on [c++ implementation](https://github.com/material-foundation/material-color-utilities/tree/main/cpp) thanks to [pybind](https://github.com/pybind).
53

@@ -13,19 +11,43 @@ It is built in reference with offical [typescript implementation](https://github
1311
Run file `tests/test_all.py` as:
1412

1513
```console
16-
python3 test_all.py <image path> <quality>
17-
14+
usage: test_all.py [-h] [--tonal-spot] [--expressive] [--fidelity] [--fruit-salad] [--monochrome] [--neutral] [--rainbow] [--vibrant]
15+
[--content] [--all] [--image IMAGE] [--quality QUALITY] [--method {pillow,cpp}]
16+
17+
Material You Color Scheme Test
18+
19+
options:
20+
-h, --help show this help message and exit
21+
--tonal-spot Print the tonal-spot dynamic scheme
22+
--expressive Print the expressive dynamic scheme
23+
--fidelity Print the fidelity dynamic scheme
24+
--fruit-salad Print the fruit-salad dynamic scheme
25+
--monochrome Print the monochrome dynamic scheme
26+
--neutral Print the neutral dynamic scheme
27+
--rainbow Print the rainbow dynamic scheme
28+
--vibrant Print the vibrant dynamic scheme
29+
--content Print the content dynamic scheme
30+
--all Print all dynamic schemes (default)
31+
--image IMAGE Path to an image file for color extraction
32+
--quality QUALITY Quality for image quantization (default: 5)
33+
--method {pillow,cpp}
34+
Method for color quantization (default: cpp)
1835
```
19-
Maximum quality is `1` that means use all pixels, and quality number more than `1` means how many pixels to skip in between while reading, also you can see it as compression.
2036

2137
<details>
2238
<summary>Click to view result</summary>
2339

2440
[Image Used, size was 8MB](https://unsplash.com/photos/zFMbpChjZGg/)
2541

26-
![image](https://github.com/T-Dynamos/materialyoucolor-pyhton/assets/68729523/9d5374c9-00b4-4b70-b82a-6792dd5c910f)
27-
![image](https://github.com/T-Dynamos/materialyoucolor-pyhton/assets/68729523/2edd819f-8600-4c82-a18a-3b759f63a552)
42+
<img width="666" height="516" alt="image" src="https://github.com/user-attachments/assets/68bc415b-3eb5-41d4-96e3-f36ced7411a8" />
43+
2844

45+
It is recommended to use the `cpp` backend, as the `pillow` backend is extremely memory-inefficient for large images.
46+
Newer Pillow APIs such as `Image.get_flattened_data()` eagerly materialize the entire image into a full list,
47+
so quality-based subsampling is applied only after full expansion and does not reduce memory usage.
48+
While `Image.getdata()` allows sampling during iteration, it is deprecated.
49+
50+
The `cpp` backend avoids these issues by operating directly on compact pixel buffers.
2951

3052
</details>
3153

@@ -50,7 +72,7 @@ pip3 install https://github.com/T-Dynamos/materialyoucolor-python/archive/master
5072
```
5173
### OS Specific
5274

53-
#### Arch Linux
75+
#### Arch Linux (OUTDATED)
5476

5577
```console
5678
yay -S python-materialyoucolor
@@ -62,7 +84,7 @@ Thanks :heart: to [@midn8hustlr](https://github.com/midn8hustlr) for this [AUR p
6284

6385
Ensure these lines in `buildozer.spec`:
6486
```python
65-
requirements = materialyoucolor
87+
requirements = materialyoucolor==3.0.0
6688
p4a.branch = develop
6789
```
6890

@@ -103,6 +125,7 @@ print(SchemeAndroid.dark(color).props)
103125
```python
104126
# Color in hue, chroma, tone form
105127
from materialyoucolor.hct import Hct
128+
from materialyoucolor.dynamiccolor.color_spec import COLOR_NAMES
106129
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
107130

108131
# There are 9 different variants of scheme.
@@ -114,60 +137,84 @@ scheme = SchemeTonalSpot( # choose any scheme here
114137
Hct.from_int(0xff4181EE), # source color in hct form
115138
True, # dark mode
116139
0.0, # contrast
140+
spec_version="2025"
117141
)
118142

119-
for color in vars(MaterialDynamicColors).keys():
120-
color_name = getattr(MaterialDynamicColors, color)
121-
if hasattr(color_name, "get_hct"): # is a color
122-
print(color, color_name.get_hct(scheme).to_rgba()) # print name of color and value in rgba format
123-
124-
# background [14, 20, 21, 255]
125-
# onBackground [222, 227, 229, 255]
126-
# surface [14, 20, 21, 255]
127-
# surfaceDim [14, 20, 21, 255]
128-
# ...
143+
mdc = MaterialDynamicColors(spec="2025")
144+
145+
for color in COLOR_NAMES:
146+
_color = getattr(mdc, color)
147+
print(color, _color.get_rgba(scheme), _color.get_hex(scheme))
148+
149+
# background [13, 14, 18, 255] #0D0E12FF
150+
# onBackground [227, 229, 240, 255] #E3E5F0FF
151+
# surface [13, 14, 18, 255] #0D0E12FF
152+
# surfaceDim [13, 14, 18, 255] #0D0E12FF
153+
# surfaceBright [41, 44, 52, 255] #292C34FF
154+
# surfaceContainerLowest [0, 0, 0, 255] #000000FF
155+
# surfaceContainerLow [17, 19, 24, 255] #111318FF
156+
# surfaceContainer [23, 25, 31, 255] #17191FFF
157+
# surfaceContainerHigh [29, 31, 38, 255] #1D1F26FF
158+
# surfaceContainerHighest [35, 38, 45, 255] #23262DFF
159+
# onSurface [227, 229, 240, 255] #E3E5F0FF
160+
# surfaceVariant [35, 38, 45, 255] #23262DFF
161+
# onSurfaceVariant [196, 198, 208, 255] #C4C6D0FF
162+
# outline [142, 144, 154, 255] #8E909AFF
163+
...
129164
```
130165

131166
- Generate and score colors from image
132167

133168
```python
134-
# Pillow is required to open image to array of pixels
135-
from PIL import Image
136-
# C++ QuantizeCelebi
137169
from materialyoucolor.quantize import QuantizeCelebi, ImageQuantizeCelebi
138-
# Material You's default scoring of colors
139170
from materialyoucolor.score.score import Score
140171

141-
# Open image
142-
image = Image.open("path_to_some_image.jpg")
143-
pixel_len = image.width * image.height
144-
image_data = image.getdata()
145-
146-
# Quality 1 means skip no pixels
172+
# Pixel subsampling factor (quality = 1 processes all pixels)
147173
quality = 1
148-
pixel_array = [image_data[_] for _ in range(0, pixel_len, quality)]
149-
150-
# Run algorithm
151-
result = QuantizeCelebi(pixel_array, 128) # 128 -> number desired colors, default 128
152174

153-
# Alternate C++ method
154-
# this is generally faster, but gives less control over image
155-
# result = ImageQuantizeCelebi("path_to_some_image.jpg", quality, 128)
175+
# Run Celebi color quantization on an image.
176+
# Returns a dict: {ARGB_color_int: population}
177+
result = ImageQuantizeCelebi(
178+
"example.jpg",
179+
quality,
180+
128, # maximum number of colors
181+
)
156182

157183
print(result)
158-
# {4278722365: 2320, 4278723396: 2405, 4278723657: 2366,...
159-
# result is a dict where key is
160-
# color in integer form (which you can convert later), and value is population
161184

162-
print(Score.score(result))
163-
# [4278722365, 4278723657]
164-
# list of selected colors in integer form
185+
# Rank and select the best theme colors.
186+
# Returns a list of ARGB color integers.
187+
selected_colors = Score.score(result)
165188

189+
print(selected_colors)
190+
# {4278911493: 276721,
191+
# 4280550664: 164247,
192+
# 4280683034: 144830,
193+
# ...
166194
```
167195
</details>
168196

197+
198+
## Gallery
199+
200+
These are some libraries which use this library for color generattion.
201+
202+
* [`dots-hyprland`](https://github.com/end-4/dots-hyprland)
203+
204+
<img width="480" height="270" alt="image" src="https://github.com/user-attachments/assets/b08ca7ac-61a1-480f-852d-cad82590006b" />
205+
206+
* [`kde-material-you-colors`](https://github.com/luisbocanegra/kde-material-you-colors)
207+
208+
<img width="480" height="270" alt="image" src="https://images.pling.com/img/00/00/67/61/06/2136963/frame-51.png" />
209+
210+
* [`EarthFM CLONE`]
211+
212+
<img width="270" height="480" alt="image" src="https://private-user-images.githubusercontent.com/68729523/516392752-7f8a2b2e-d81f-4c2c-8550-c55dea07c5bb.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Njk4NzQyNTksIm5iZiI6MTc2OTg3Mzk1OSwicGF0aCI6Ii82ODcyOTUyMy81MTYzOTI3NTItN2Y4YTJiMmUtZDgxZi00YzJjLTg1NTAtYzU1ZGVhMDdjNWJiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjAxMzElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwMTMxVDE1MzkxOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWQyZmVlMTllZThjYjgxN2M1ZjVhYmFjYjRjMmM3MTZmMDQ3ZTdkODI2YTQwZGNmNWNhNWE3MjJmMWFhZDg2YmMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0._zSJgVinO01zMsG8pgJ7x0NZ914vVNy2vNnN8b7BoCA" />
213+
214+
> This is my private project where the theme colors change based on the album art.
215+
169216
## FAQ
170217

171218
1. How it is different from `avanisubbiah/material-color-utilities`?
172219

173-
See https://github.com/T-Dynamos/materialyoucolor-python/issues/3
220+
This library is up to date, fast, and uses a hybrid system for image generation.

materialyoucolor/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "2.0.10"
1+
__version__ = "3.0.0"

0 commit comments

Comments
 (0)