-
Notifications
You must be signed in to change notification settings - Fork 2
/
imgcry.py
102 lines (85 loc) · 3.6 KB
/
imgcry.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# -*- coding: utf-8 -*-
import math
import re
import time
from io import BytesIO
from PIL import Image, ImageOps
# https://github.com/xfgryujk/weibo-img-crypto/blob/8083e7288d188e430ba84aa33c2f01afefa90523/src/random.js#L1
class Random:
def __init__(self, seed=None):
self._rng_state = [0, 0]
self._set_rng_state(seed)
def _set_rng_state(self, seed=None):
if seed is None:
seed = str(int(time.time() * 1000))
else:
seed = str(seed)
if re.fullmatch(r'^-?\d{1,10}$', seed) and -0x80000000 <= int(seed) <= 0x7FFFFFFF:
seed = int(seed)
else:
seed = self._hash_code(seed)
self._rng_state = [seed & 0xFFFF, (seed & 0xFFFFFFFF) >> 16]
@staticmethod
def _hash_code(s):
hash_ = 0
for c in s:
hash_ = (hash_ * 31 + ord(c)) & 0xFFFFFFFF
return hash_
def random(self):
"""返回[0, 1)"""
r0 = (18030 * (self._rng_state[0] & 0xFFFF) + ((self._rng_state[0] & 0xFFFFFFFF) >> 16)) | 0
self._rng_state[0] = r0
r1 = (36969 * (self._rng_state[1] & 0xFFFF) + ((self._rng_state[1] & 0xFFFFFFFF) >> 16)) | 0
self._rng_state[1] = r1
x = (((r0 << 16) & 0xFFFFFFFF) + (r1 & 0xFFFF)) | 0
return ((x + 0x100000000) if x < 0 else x) * 2.3283064365386962890625e-10
def randint(self, min_, max_):
"""返回[min, max]的整数"""
return int(math.floor(min_ + self.random() * (max_ - min_ + 1)))
class RandomSequence:
def __init__(self, length, seed):
self._rng = Random(seed)
self._list = list(range(length))
self._next_min = 0
def next(self):
if self._next_min >= len(self._list):
self._next_min = 0
index = self._rng.randint(self._next_min, len(self._list) - 1)
result = self._list[index]
self._list[index] = self._list[self._next_min]
self._list[self._next_min] = result
self._next_min += 1
return result
def encrypt_image(data, seed=114514):
f = BytesIO(data)
img = Image.open(f)
# 最短边长超过1080的会被微博压缩
min_size = min(img.width, img.height)
if min_size > 1080:
scale = 1080 / min_size
img = img.resize((round(img.width * scale), round(img.height * scale)), Image.BICUBIC)
# https://github.com/xfgryujk/weibo-img-crypto/blob/8083e7288d188e430ba84aa33c2f01afefa90523/src/codec.js#L160
# block_width = img.width // 8
# block_height = img.height // 8
# new_img = Image.new('RGB', (block_width * 8, block_height * 8))
# seq = RandomSequence(block_width * block_height, seed)
# for block_y in range(block_height):
# for block_x in range(block_width):
# index = seq.next()
# new_block_x = index % block_width
# new_block_y = index // block_width
# block = img.crop((block_x * 8, block_y * 8, (block_x + 1) * 8, (block_y + 1) * 8))
# new_img.paste(block, (new_block_x * 8, new_block_y * 8))
# https://github.com/xfgryujk/weibo-img-crypto/blob/dc9b5f2a8a2163ac11d076aaac3d68a03a49b795/src/codec.js#L206
invert_first = True
for y in range(0, img.height, 8):
height = min(8, img.height - y)
for x in range(0 if invert_first else 8, img.width, 16):
width = min(8, img.width - x)
block = img.crop((x, y, x + width, y + height))
block = ImageOps.invert(block)
img.paste(block, (x, y))
invert_first = not invert_first
f = BytesIO()
img.save(f, 'JPEG', quality='maximum') # 大概减少一半文件尺寸
return f.getvalue()