dl1wjd2 commited on
Commit
c76679e
·
verified ·
1 Parent(s): 94bbf81

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +12 -0
  2. hug/bin/Activate.ps1 +247 -0
  3. hug/bin/activate +70 -0
  4. hug/bin/activate.csh +27 -0
  5. hug/bin/activate.fish +69 -0
  6. hug/bin/f2py +6 -0
  7. hug/bin/fonttools +6 -0
  8. hug/bin/hf +6 -0
  9. hug/bin/httpx +6 -0
  10. hug/bin/httpx2 +6 -0
  11. hug/bin/idna +6 -0
  12. hug/bin/isympy +6 -0
  13. hug/bin/normalizer +6 -0
  14. hug/bin/numpy-config +6 -0
  15. hug/bin/pip +8 -0
  16. hug/bin/pip3 +8 -0
  17. hug/bin/pip3.12 +8 -0
  18. hug/bin/proton +6 -0
  19. hug/bin/proton-viewer +6 -0
  20. hug/bin/pyftmerge +6 -0
  21. hug/bin/pyftsubset +6 -0
  22. hug/bin/python +3 -0
  23. hug/bin/python3 +3 -0
  24. hug/bin/python3.12 +3 -0
  25. hug/bin/tiny-agents +6 -0
  26. hug/bin/torchfrtrace +6 -0
  27. hug/bin/torchrun +6 -0
  28. hug/bin/tqdm +6 -0
  29. hug/bin/ttx +6 -0
  30. hug/bin/ul +6 -0
  31. hug/bin/ultralytics +6 -0
  32. hug/bin/yolo +6 -0
  33. hug/lib/python3.12/site-packages/PIL/AvifImagePlugin.py +299 -0
  34. hug/lib/python3.12/site-packages/PIL/BdfFontFile.py +122 -0
  35. hug/lib/python3.12/site-packages/PIL/BlpImagePlugin.py +503 -0
  36. hug/lib/python3.12/site-packages/PIL/BmpImagePlugin.py +526 -0
  37. hug/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py +70 -0
  38. hug/lib/python3.12/site-packages/PIL/ContainerIO.py +177 -0
  39. hug/lib/python3.12/site-packages/PIL/CurImagePlugin.py +105 -0
  40. hug/lib/python3.12/site-packages/PIL/DcxImagePlugin.py +88 -0
  41. hug/lib/python3.12/site-packages/PIL/DdsImagePlugin.py +637 -0
  42. hug/lib/python3.12/site-packages/PIL/EpsImagePlugin.py +494 -0
  43. hug/lib/python3.12/site-packages/PIL/ExifTags.py +383 -0
  44. hug/lib/python3.12/site-packages/PIL/FitsImagePlugin.py +171 -0
  45. hug/lib/python3.12/site-packages/PIL/FliImagePlugin.py +188 -0
  46. hug/lib/python3.12/site-packages/PIL/FontFile.py +163 -0
  47. hug/lib/python3.12/site-packages/PIL/FpxImagePlugin.py +264 -0
  48. hug/lib/python3.12/site-packages/PIL/FtexImagePlugin.py +119 -0
  49. hug/lib/python3.12/site-packages/PIL/GbrImagePlugin.py +101 -0
  50. hug/lib/python3.12/site-packages/PIL/GdImageFile.py +109 -0
.gitattributes CHANGED
@@ -33,3 +33,15 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ hug/bin/python filter=lfs diff=lfs merge=lfs -text
37
+ hug/bin/python3 filter=lfs diff=lfs merge=lfs -text
38
+ hug/bin/python3.12 filter=lfs diff=lfs merge=lfs -text
39
+ hug/lib/python3.12/site-packages/PIL/__pycache__/Image.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
40
+ hug/lib/python3.12/site-packages/PIL/__pycache__/TiffImagePlugin.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
41
+ hug/lib/python3.12/site-packages/PIL/_imaging.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
42
+ hug/lib/python3.12/site-packages/PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
43
+ hug/lib/python3.12/site-packages/PIL/_imagingft.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
44
+ hug/lib/python3.12/site-packages/PIL/_imagingmath.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
45
+ hug/lib/python3.12/site-packages/PIL/_webp.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
46
+ hug/lib/python3.12/site-packages/__pycache__/pynvml.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
47
+ hug/lib/python3.12/site-packages/__pycache__/typing_extensions.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
hug/bin/Activate.ps1 ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <#
2
+ .Synopsis
3
+ Activate a Python virtual environment for the current PowerShell session.
4
+
5
+ .Description
6
+ Pushes the python executable for a virtual environment to the front of the
7
+ $Env:PATH environment variable and sets the prompt to signify that you are
8
+ in a Python virtual environment. Makes use of the command line switches as
9
+ well as the `pyvenv.cfg` file values present in the virtual environment.
10
+
11
+ .Parameter VenvDir
12
+ Path to the directory that contains the virtual environment to activate. The
13
+ default value for this is the parent of the directory that the Activate.ps1
14
+ script is located within.
15
+
16
+ .Parameter Prompt
17
+ The prompt prefix to display when this virtual environment is activated. By
18
+ default, this prompt is the name of the virtual environment folder (VenvDir)
19
+ surrounded by parentheses and followed by a single space (ie. '(.venv) ').
20
+
21
+ .Example
22
+ Activate.ps1
23
+ Activates the Python virtual environment that contains the Activate.ps1 script.
24
+
25
+ .Example
26
+ Activate.ps1 -Verbose
27
+ Activates the Python virtual environment that contains the Activate.ps1 script,
28
+ and shows extra information about the activation as it executes.
29
+
30
+ .Example
31
+ Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
32
+ Activates the Python virtual environment located in the specified location.
33
+
34
+ .Example
35
+ Activate.ps1 -Prompt "MyPython"
36
+ Activates the Python virtual environment that contains the Activate.ps1 script,
37
+ and prefixes the current prompt with the specified string (surrounded in
38
+ parentheses) while the virtual environment is active.
39
+
40
+ .Notes
41
+ On Windows, it may be required to enable this Activate.ps1 script by setting the
42
+ execution policy for the user. You can do this by issuing the following PowerShell
43
+ command:
44
+
45
+ PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
46
+
47
+ For more information on Execution Policies:
48
+ https://go.microsoft.com/fwlink/?LinkID=135170
49
+
50
+ #>
51
+ Param(
52
+ [Parameter(Mandatory = $false)]
53
+ [String]
54
+ $VenvDir,
55
+ [Parameter(Mandatory = $false)]
56
+ [String]
57
+ $Prompt
58
+ )
59
+
60
+ <# Function declarations --------------------------------------------------- #>
61
+
62
+ <#
63
+ .Synopsis
64
+ Remove all shell session elements added by the Activate script, including the
65
+ addition of the virtual environment's Python executable from the beginning of
66
+ the PATH variable.
67
+
68
+ .Parameter NonDestructive
69
+ If present, do not remove this function from the global namespace for the
70
+ session.
71
+
72
+ #>
73
+ function global:deactivate ([switch]$NonDestructive) {
74
+ # Revert to original values
75
+
76
+ # The prior prompt:
77
+ if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
78
+ Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
79
+ Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
80
+ }
81
+
82
+ # The prior PYTHONHOME:
83
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
84
+ Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
85
+ Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
86
+ }
87
+
88
+ # The prior PATH:
89
+ if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
90
+ Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
91
+ Remove-Item -Path Env:_OLD_VIRTUAL_PATH
92
+ }
93
+
94
+ # Just remove the VIRTUAL_ENV altogether:
95
+ if (Test-Path -Path Env:VIRTUAL_ENV) {
96
+ Remove-Item -Path env:VIRTUAL_ENV
97
+ }
98
+
99
+ # Just remove VIRTUAL_ENV_PROMPT altogether.
100
+ if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
101
+ Remove-Item -Path env:VIRTUAL_ENV_PROMPT
102
+ }
103
+
104
+ # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
105
+ if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
106
+ Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
107
+ }
108
+
109
+ # Leave deactivate function in the global namespace if requested:
110
+ if (-not $NonDestructive) {
111
+ Remove-Item -Path function:deactivate
112
+ }
113
+ }
114
+
115
+ <#
116
+ .Description
117
+ Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
118
+ given folder, and returns them in a map.
119
+
120
+ For each line in the pyvenv.cfg file, if that line can be parsed into exactly
121
+ two strings separated by `=` (with any amount of whitespace surrounding the =)
122
+ then it is considered a `key = value` line. The left hand string is the key,
123
+ the right hand is the value.
124
+
125
+ If the value starts with a `'` or a `"` then the first and last character is
126
+ stripped from the value before being captured.
127
+
128
+ .Parameter ConfigDir
129
+ Path to the directory that contains the `pyvenv.cfg` file.
130
+ #>
131
+ function Get-PyVenvConfig(
132
+ [String]
133
+ $ConfigDir
134
+ ) {
135
+ Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
136
+
137
+ # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
138
+ $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
139
+
140
+ # An empty map will be returned if no config file is found.
141
+ $pyvenvConfig = @{ }
142
+
143
+ if ($pyvenvConfigPath) {
144
+
145
+ Write-Verbose "File exists, parse `key = value` lines"
146
+ $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
147
+
148
+ $pyvenvConfigContent | ForEach-Object {
149
+ $keyval = $PSItem -split "\s*=\s*", 2
150
+ if ($keyval[0] -and $keyval[1]) {
151
+ $val = $keyval[1]
152
+
153
+ # Remove extraneous quotations around a string value.
154
+ if ("'""".Contains($val.Substring(0, 1))) {
155
+ $val = $val.Substring(1, $val.Length - 2)
156
+ }
157
+
158
+ $pyvenvConfig[$keyval[0]] = $val
159
+ Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
160
+ }
161
+ }
162
+ }
163
+ return $pyvenvConfig
164
+ }
165
+
166
+
167
+ <# Begin Activate script --------------------------------------------------- #>
168
+
169
+ # Determine the containing directory of this script
170
+ $VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
171
+ $VenvExecDir = Get-Item -Path $VenvExecPath
172
+
173
+ Write-Verbose "Activation script is located in path: '$VenvExecPath'"
174
+ Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
175
+ Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
176
+
177
+ # Set values required in priority: CmdLine, ConfigFile, Default
178
+ # First, get the location of the virtual environment, it might not be
179
+ # VenvExecDir if specified on the command line.
180
+ if ($VenvDir) {
181
+ Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
182
+ }
183
+ else {
184
+ Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
185
+ $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
186
+ Write-Verbose "VenvDir=$VenvDir"
187
+ }
188
+
189
+ # Next, read the `pyvenv.cfg` file to determine any required value such
190
+ # as `prompt`.
191
+ $pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
192
+
193
+ # Next, set the prompt from the command line, or the config file, or
194
+ # just use the name of the virtual environment folder.
195
+ if ($Prompt) {
196
+ Write-Verbose "Prompt specified as argument, using '$Prompt'"
197
+ }
198
+ else {
199
+ Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
200
+ if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
201
+ Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
202
+ $Prompt = $pyvenvCfg['prompt'];
203
+ }
204
+ else {
205
+ Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
206
+ Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
207
+ $Prompt = Split-Path -Path $venvDir -Leaf
208
+ }
209
+ }
210
+
211
+ Write-Verbose "Prompt = '$Prompt'"
212
+ Write-Verbose "VenvDir='$VenvDir'"
213
+
214
+ # Deactivate any currently active virtual environment, but leave the
215
+ # deactivate function in place.
216
+ deactivate -nondestructive
217
+
218
+ # Now set the environment variable VIRTUAL_ENV, used by many tools to determine
219
+ # that there is an activated venv.
220
+ $env:VIRTUAL_ENV = $VenvDir
221
+
222
+ if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
223
+
224
+ Write-Verbose "Setting prompt to '$Prompt'"
225
+
226
+ # Set the prompt to include the env name
227
+ # Make sure _OLD_VIRTUAL_PROMPT is global
228
+ function global:_OLD_VIRTUAL_PROMPT { "" }
229
+ Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
230
+ New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
231
+
232
+ function global:prompt {
233
+ Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
234
+ _OLD_VIRTUAL_PROMPT
235
+ }
236
+ $env:VIRTUAL_ENV_PROMPT = $Prompt
237
+ }
238
+
239
+ # Clear PYTHONHOME
240
+ if (Test-Path -Path Env:PYTHONHOME) {
241
+ Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
242
+ Remove-Item -Path Env:PYTHONHOME
243
+ }
244
+
245
+ # Add the venv to the PATH
246
+ Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
247
+ $Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
hug/bin/activate ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file must be used with "source bin/activate" *from bash*
2
+ # You cannot run it directly
3
+
4
+ deactivate () {
5
+ # reset old environment variables
6
+ if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
7
+ PATH="${_OLD_VIRTUAL_PATH:-}"
8
+ export PATH
9
+ unset _OLD_VIRTUAL_PATH
10
+ fi
11
+ if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
12
+ PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
13
+ export PYTHONHOME
14
+ unset _OLD_VIRTUAL_PYTHONHOME
15
+ fi
16
+
17
+ # Call hash to forget past commands. Without forgetting
18
+ # past commands the $PATH changes we made may not be respected
19
+ hash -r 2> /dev/null
20
+
21
+ if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
22
+ PS1="${_OLD_VIRTUAL_PS1:-}"
23
+ export PS1
24
+ unset _OLD_VIRTUAL_PS1
25
+ fi
26
+
27
+ unset VIRTUAL_ENV
28
+ unset VIRTUAL_ENV_PROMPT
29
+ if [ ! "${1:-}" = "nondestructive" ] ; then
30
+ # Self destruct!
31
+ unset -f deactivate
32
+ fi
33
+ }
34
+
35
+ # unset irrelevant variables
36
+ deactivate nondestructive
37
+
38
+ # on Windows, a path can contain colons and backslashes and has to be converted:
39
+ if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
40
+ # transform D:\path\to\venv to /d/path/to/venv on MSYS
41
+ # and to /cygdrive/d/path/to/venv on Cygwin
42
+ export VIRTUAL_ENV=$(cygpath /home/jhlee/Hugging_Face/hug)
43
+ else
44
+ # use the path as-is
45
+ export VIRTUAL_ENV=/home/jhlee/Hugging_Face/hug
46
+ fi
47
+
48
+ _OLD_VIRTUAL_PATH="$PATH"
49
+ PATH="$VIRTUAL_ENV/"bin":$PATH"
50
+ export PATH
51
+
52
+ # unset PYTHONHOME if set
53
+ # this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
54
+ # could use `if (set -u; : $PYTHONHOME) ;` in bash
55
+ if [ -n "${PYTHONHOME:-}" ] ; then
56
+ _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
57
+ unset PYTHONHOME
58
+ fi
59
+
60
+ if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
61
+ _OLD_VIRTUAL_PS1="${PS1:-}"
62
+ PS1='(hug) '"${PS1:-}"
63
+ export PS1
64
+ VIRTUAL_ENV_PROMPT='(hug) '
65
+ export VIRTUAL_ENV_PROMPT
66
+ fi
67
+
68
+ # Call hash to forget past commands. Without forgetting
69
+ # past commands the $PATH changes we made may not be respected
70
+ hash -r 2> /dev/null
hug/bin/activate.csh ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file must be used with "source bin/activate.csh" *from csh*.
2
+ # You cannot run it directly.
3
+
4
+ # Created by Davide Di Blasi <davidedb@gmail.com>.
5
+ # Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
6
+
7
+ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
8
+
9
+ # Unset irrelevant variables.
10
+ deactivate nondestructive
11
+
12
+ setenv VIRTUAL_ENV /home/jhlee/Hugging_Face/hug
13
+
14
+ set _OLD_VIRTUAL_PATH="$PATH"
15
+ setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
16
+
17
+
18
+ set _OLD_VIRTUAL_PROMPT="$prompt"
19
+
20
+ if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
21
+ set prompt = '(hug) '"$prompt"
22
+ setenv VIRTUAL_ENV_PROMPT '(hug) '
23
+ endif
24
+
25
+ alias pydoc python -m pydoc
26
+
27
+ rehash
hug/bin/activate.fish ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file must be used with "source <venv>/bin/activate.fish" *from fish*
2
+ # (https://fishshell.com/). You cannot run it directly.
3
+
4
+ function deactivate -d "Exit virtual environment and return to normal shell environment"
5
+ # reset old environment variables
6
+ if test -n "$_OLD_VIRTUAL_PATH"
7
+ set -gx PATH $_OLD_VIRTUAL_PATH
8
+ set -e _OLD_VIRTUAL_PATH
9
+ end
10
+ if test -n "$_OLD_VIRTUAL_PYTHONHOME"
11
+ set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
12
+ set -e _OLD_VIRTUAL_PYTHONHOME
13
+ end
14
+
15
+ if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
16
+ set -e _OLD_FISH_PROMPT_OVERRIDE
17
+ # prevents error when using nested fish instances (Issue #93858)
18
+ if functions -q _old_fish_prompt
19
+ functions -e fish_prompt
20
+ functions -c _old_fish_prompt fish_prompt
21
+ functions -e _old_fish_prompt
22
+ end
23
+ end
24
+
25
+ set -e VIRTUAL_ENV
26
+ set -e VIRTUAL_ENV_PROMPT
27
+ if test "$argv[1]" != "nondestructive"
28
+ # Self-destruct!
29
+ functions -e deactivate
30
+ end
31
+ end
32
+
33
+ # Unset irrelevant variables.
34
+ deactivate nondestructive
35
+
36
+ set -gx VIRTUAL_ENV /home/jhlee/Hugging_Face/hug
37
+
38
+ set -gx _OLD_VIRTUAL_PATH $PATH
39
+ set -gx PATH "$VIRTUAL_ENV/"bin $PATH
40
+
41
+ # Unset PYTHONHOME if set.
42
+ if set -q PYTHONHOME
43
+ set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
44
+ set -e PYTHONHOME
45
+ end
46
+
47
+ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
48
+ # fish uses a function instead of an env var to generate the prompt.
49
+
50
+ # Save the current fish_prompt function as the function _old_fish_prompt.
51
+ functions -c fish_prompt _old_fish_prompt
52
+
53
+ # With the original prompt function renamed, we can override with our own.
54
+ function fish_prompt
55
+ # Save the return status of the last command.
56
+ set -l old_status $status
57
+
58
+ # Output the venv prompt; color taken from the blue of the Python logo.
59
+ printf "%s%s%s" (set_color 4B8BBE) '(hug) ' (set_color normal)
60
+
61
+ # Restore the return status of the previous command.
62
+ echo "exit $old_status" | .
63
+ # Output the original/"old" prompt.
64
+ _old_fish_prompt
65
+ end
66
+
67
+ set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
68
+ set -gx VIRTUAL_ENV_PROMPT '(hug) '
69
+ end
hug/bin/f2py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from numpy.f2py.f2py2e import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/fonttools ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from fontTools.__main__ import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/hf ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from huggingface_hub.cli.hf import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/httpx ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from httpx import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/httpx2 ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from httpx2._main import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/idna ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from idna.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/isympy ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from isympy import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/normalizer ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from charset_normalizer.cli import cli_detect
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(cli_detect())
hug/bin/numpy-config ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from numpy._configtool import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/pip ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ import re
4
+ import sys
5
+ from pip._internal.cli.main import main
6
+ if __name__ == '__main__':
7
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
8
+ sys.exit(main())
hug/bin/pip3 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ import re
4
+ import sys
5
+ from pip._internal.cli.main import main
6
+ if __name__ == '__main__':
7
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
8
+ sys.exit(main())
hug/bin/pip3.12 ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ # -*- coding: utf-8 -*-
3
+ import re
4
+ import sys
5
+ from pip._internal.cli.main import main
6
+ if __name__ == '__main__':
7
+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
8
+ sys.exit(main())
hug/bin/proton ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from triton.profiler.proton import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/proton-viewer ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from triton.profiler.viewer import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/pyftmerge ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from fontTools.merge import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/pyftsubset ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from fontTools.subset import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/python ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f
3
+ size 8020928
hug/bin/python3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f
3
+ size 8020928
hug/bin/python3.12 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e50d468e8b0adfb05733f5b87b3cff34829c4a8c1aea50c865aa8bdfe4bb150f
3
+ size 8020928
hug/bin/tiny-agents ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from huggingface_hub.inference._mcp.cli import app
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(app())
hug/bin/torchfrtrace ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from torch.distributed.flight_recorder.fr_trace import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/torchrun ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from torch.distributed.run import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/tqdm ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from tqdm.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/ttx ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from fontTools.ttx import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/ul ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from ultralytics_platform.cli import main
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(main())
hug/bin/ultralytics ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from ultralytics.cfg import entrypoint
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(entrypoint())
hug/bin/yolo ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/home/jhlee/Hugging_Face/hug/bin/python
2
+ import sys
3
+ from ultralytics.cfg import entrypoint
4
+ if __name__ == '__main__':
5
+ sys.argv[0] = sys.argv[0].removesuffix('.exe')
6
+ sys.exit(entrypoint())
hug/lib/python3.12/site-packages/PIL/AvifImagePlugin.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ __lazy_modules__ = {"io", "typing"}
4
+
5
+ import os
6
+ from io import BytesIO
7
+ from typing import IO
8
+
9
+ from . import ExifTags, Image, ImageFile, ImageSequence
10
+
11
+ try:
12
+ from . import _avif
13
+
14
+ SUPPORTED = True
15
+ except ImportError:
16
+ SUPPORTED = False
17
+
18
+ # Decoder options as module globals, until there is a way to pass parameters
19
+ # to Image.open (see https://github.com/python-pillow/Pillow/issues/569)
20
+ DECODE_CODEC_CHOICE = "auto"
21
+ DEFAULT_MAX_THREADS = 0
22
+
23
+
24
+ def get_codec_version(codec_name: str) -> str | None:
25
+ versions = _avif.codec_versions()
26
+ for version in versions.split(", "):
27
+ if version.split(" [")[0] == codec_name:
28
+ return version.split(":")[-1].split(" ")[0]
29
+ return None
30
+
31
+
32
+ def _accept(prefix: bytes) -> bool | str:
33
+ if prefix[4:8] != b"ftyp":
34
+ return False
35
+ major_brand = prefix[8:12]
36
+ if major_brand in (
37
+ # coding brands
38
+ b"avif",
39
+ b"avis",
40
+ # We accept files with AVIF container brands; we can't yet know if
41
+ # the ftyp box has the correct compatible brands, but if it doesn't
42
+ # then the plugin will raise a SyntaxError which Pillow will catch
43
+ # before moving on to the next plugin that accepts the file.
44
+ #
45
+ # Also, because this file might not actually be an AVIF file, we
46
+ # don't raise an error if AVIF support isn't properly compiled.
47
+ b"mif1",
48
+ b"msf1",
49
+ ):
50
+ if not SUPPORTED:
51
+ return (
52
+ "image file could not be identified because AVIF support not installed"
53
+ )
54
+ return True
55
+ return False
56
+
57
+
58
+ def _get_default_max_threads() -> int:
59
+ if DEFAULT_MAX_THREADS:
60
+ return DEFAULT_MAX_THREADS
61
+ if hasattr(os, "sched_getaffinity"):
62
+ return len(os.sched_getaffinity(0))
63
+ else:
64
+ return os.cpu_count() or 1
65
+
66
+
67
+ class AvifImageFile(ImageFile.ImageFile):
68
+ format = "AVIF"
69
+ format_description = "AVIF image"
70
+ __frame = -1
71
+
72
+ def _open(self) -> None:
73
+ if not SUPPORTED:
74
+ msg = "image file could not be opened because AVIF support not installed"
75
+ raise SyntaxError(msg)
76
+
77
+ if DECODE_CODEC_CHOICE != "auto" and not _avif.decoder_codec_available(
78
+ DECODE_CODEC_CHOICE
79
+ ):
80
+ msg = "Invalid opening codec"
81
+ raise ValueError(msg)
82
+
83
+ assert self.fp is not None
84
+ self._decoder = _avif.AvifDecoder(
85
+ self.fp.read(),
86
+ DECODE_CODEC_CHOICE,
87
+ _get_default_max_threads(),
88
+ )
89
+
90
+ # Get info from decoder
91
+ self._size, self.n_frames, self._mode, icc, exif, exif_orientation, xmp = (
92
+ self._decoder.get_info()
93
+ )
94
+ self.is_animated = self.n_frames > 1
95
+
96
+ if icc:
97
+ self.info["icc_profile"] = icc
98
+ if xmp:
99
+ self.info["xmp"] = xmp
100
+
101
+ if exif_orientation != 1 or exif:
102
+ exif_data = Image.Exif()
103
+ if exif:
104
+ exif_data.load(exif)
105
+ original_orientation = exif_data.get(ExifTags.Base.Orientation, 1)
106
+ else:
107
+ original_orientation = 1
108
+ if exif_orientation != original_orientation:
109
+ exif_data[ExifTags.Base.Orientation] = exif_orientation
110
+ exif = exif_data.tobytes()
111
+ if exif:
112
+ self.info["exif"] = exif
113
+ self.seek(0)
114
+
115
+ def seek(self, frame: int) -> None:
116
+ if not self._seek_check(frame):
117
+ return
118
+ self.info.pop("timestamp", None)
119
+ self.info.pop("duration", None)
120
+
121
+ # Set tile
122
+ self.__frame = frame
123
+ self.tile = [ImageFile._Tile("raw", (0, 0, *self.size), 0, self.mode)]
124
+
125
+ def load(self) -> Image.core.PixelAccess | None:
126
+ if self.tile:
127
+ # We need to load the image data for this frame
128
+ data, timescale, pts_in_timescales, duration_in_timescales = (
129
+ self._decoder.get_frame(self.__frame)
130
+ )
131
+ self.info["timestamp"] = round(1000 * (pts_in_timescales / timescale))
132
+ self.info["duration"] = round(1000 * (duration_in_timescales / timescale))
133
+
134
+ if self.fp and self._exclusive_fp:
135
+ self.fp.close()
136
+ self.fp = BytesIO(data)
137
+
138
+ return super().load()
139
+
140
+ def load_seek(self, pos: int) -> None:
141
+ pass
142
+
143
+ def tell(self) -> int:
144
+ return self.__frame
145
+
146
+
147
+ def _save_all(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
148
+ _save(im, fp, filename, save_all=True)
149
+
150
+
151
+ def _save(
152
+ im: Image.Image, fp: IO[bytes], filename: str | bytes, save_all: bool = False
153
+ ) -> None:
154
+ info = im.encoderinfo.copy()
155
+ if save_all:
156
+ append_images = list(info.get("append_images", []))
157
+ else:
158
+ append_images = []
159
+
160
+ grayscale_modes = {"1", "L", "I", "I;16", "I;16L", "I;16B", "I;16N", "F"}
161
+ grayscale = all(
162
+ frame.mode in grayscale_modes
163
+ for ims in [im] + append_images
164
+ for frame in ImageSequence.Iterator(ims)
165
+ )
166
+
167
+ quality = info.get("quality", 75)
168
+ if not isinstance(quality, int) or quality < 0 or quality > 100:
169
+ msg = "Invalid quality setting"
170
+ raise ValueError(msg)
171
+
172
+ duration = info.get("duration", 0)
173
+ subsampling = info.get("subsampling", "4:0:0" if grayscale else "4:2:0")
174
+ speed = info.get("speed", 6)
175
+ max_threads = info.get("max_threads", _get_default_max_threads())
176
+ codec = info.get("codec", "auto")
177
+ if codec != "auto" and not _avif.encoder_codec_available(codec):
178
+ msg = "Invalid saving codec"
179
+ raise ValueError(msg)
180
+ range_ = info.get("range", "full")
181
+ tile_rows_log2 = info.get("tile_rows", 0)
182
+ tile_cols_log2 = info.get("tile_cols", 0)
183
+ alpha_premultiplied = bool(info.get("alpha_premultiplied", False))
184
+ autotiling = bool(info.get("autotiling", tile_rows_log2 == tile_cols_log2 == 0))
185
+
186
+ icc_profile = info.get("icc_profile", im.info.get("icc_profile"))
187
+ exif_orientation = 1
188
+ if exif := info.get("exif"):
189
+ if isinstance(exif, Image.Exif):
190
+ exif_data = exif
191
+ else:
192
+ exif_data = Image.Exif()
193
+ exif_data.load(exif)
194
+ if ExifTags.Base.Orientation in exif_data:
195
+ exif_orientation = exif_data.pop(ExifTags.Base.Orientation)
196
+ exif = exif_data.tobytes() if exif_data else b""
197
+ elif isinstance(exif, Image.Exif):
198
+ exif = exif_data.tobytes()
199
+
200
+ xmp = info.get("xmp")
201
+
202
+ if isinstance(xmp, str):
203
+ xmp = xmp.encode("utf-8")
204
+
205
+ advanced = info.get("advanced")
206
+ if advanced is not None:
207
+ if isinstance(advanced, dict):
208
+ advanced = advanced.items()
209
+ try:
210
+ advanced = tuple(advanced)
211
+ except TypeError:
212
+ invalid = True
213
+ else:
214
+ invalid = any(not isinstance(v, tuple) or len(v) != 2 for v in advanced)
215
+ if invalid:
216
+ msg = (
217
+ "advanced codec options must be a dict of key-value string "
218
+ "pairs or a series of key-value two-tuples"
219
+ )
220
+ raise ValueError(msg)
221
+
222
+ # Setup the AVIF encoder
223
+ enc = _avif.AvifEncoder(
224
+ im.size,
225
+ subsampling,
226
+ quality,
227
+ speed,
228
+ max_threads,
229
+ codec,
230
+ range_,
231
+ tile_rows_log2,
232
+ tile_cols_log2,
233
+ alpha_premultiplied,
234
+ autotiling,
235
+ icc_profile or b"",
236
+ exif or b"",
237
+ exif_orientation,
238
+ xmp or b"",
239
+ advanced,
240
+ )
241
+
242
+ # Add each frame
243
+ frame_idx = 0
244
+ frame_duration = 0
245
+ cur_idx = im.tell()
246
+ is_single_frame = not append_images and not getattr(im, "is_animated", False)
247
+ try:
248
+ for ims in [im] + append_images:
249
+ for frame in ImageSequence.Iterator(ims):
250
+ # Make sure image mode is supported
251
+ rawmode = frame.mode
252
+ if ims.mode not in {"L", "RGB", "RGBA"}:
253
+ if ims.has_transparency_data:
254
+ rawmode = "RGBA"
255
+ elif ims.mode in grayscale_modes:
256
+ rawmode = "L"
257
+ else:
258
+ rawmode = "RGB"
259
+ frame = frame.convert(rawmode)
260
+
261
+ # Update frame duration
262
+ if isinstance(duration, (list, tuple)):
263
+ frame_duration = duration[frame_idx]
264
+ else:
265
+ frame_duration = duration
266
+
267
+ # Append the frame to the animation encoder
268
+ enc.add(
269
+ frame.tobytes("raw", rawmode),
270
+ frame_duration,
271
+ frame.size,
272
+ rawmode,
273
+ is_single_frame,
274
+ )
275
+
276
+ # Update frame index
277
+ frame_idx += 1
278
+
279
+ if not save_all:
280
+ break
281
+
282
+ finally:
283
+ im.seek(cur_idx)
284
+
285
+ # Get the final output from the encoder
286
+ data = enc.finish()
287
+ if data is None:
288
+ msg = "cannot write file as AVIF (encoder returned None)"
289
+ raise OSError(msg)
290
+
291
+ fp.write(data)
292
+
293
+
294
+ Image.register_open(AvifImageFile.format, AvifImageFile, _accept)
295
+ if SUPPORTED:
296
+ Image.register_save(AvifImageFile.format, _save)
297
+ Image.register_save_all(AvifImageFile.format, _save_all)
298
+ Image.register_extensions(AvifImageFile.format, [".avif", ".avifs"])
299
+ Image.register_mime(AvifImageFile.format, "image/avif")
hug/lib/python3.12/site-packages/PIL/BdfFontFile.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library
3
+ # $Id$
4
+ #
5
+ # bitmap distribution font (bdf) file parser
6
+ #
7
+ # history:
8
+ # 1996-05-16 fl created (as bdf2pil)
9
+ # 1997-08-25 fl converted to FontFile driver
10
+ # 2001-05-25 fl removed bogus __init__ call
11
+ # 2002-11-20 fl robustification (from Kevin Cazabon, Dmitry Vasiliev)
12
+ # 2003-04-22 fl more robustification (from Graham Dumpleton)
13
+ #
14
+ # Copyright (c) 1997-2003 by Secret Labs AB.
15
+ # Copyright (c) 1997-2003 by Fredrik Lundh.
16
+ #
17
+ # See the README file for information on usage and redistribution.
18
+ #
19
+
20
+ """
21
+ Parse X Bitmap Distribution Format (BDF)
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from . import FontFile, Image
27
+
28
+ TYPE_CHECKING = False
29
+ if TYPE_CHECKING:
30
+ from typing import BinaryIO
31
+
32
+
33
+ def bdf_char(
34
+ f: BinaryIO,
35
+ ) -> (
36
+ tuple[
37
+ str,
38
+ int,
39
+ tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]],
40
+ Image.Image,
41
+ ]
42
+ | None
43
+ ):
44
+ # skip to STARTCHAR
45
+ while True:
46
+ s = f.readline()
47
+ if not s:
48
+ return None
49
+ if s.startswith(b"STARTCHAR"):
50
+ break
51
+ id = s[9:].strip().decode("ascii")
52
+
53
+ # load symbol properties
54
+ props = {}
55
+ while True:
56
+ s = f.readline()
57
+ if not s or s.startswith(b"BITMAP"):
58
+ break
59
+ i = s.find(b" ")
60
+ props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
61
+
62
+ # load bitmap
63
+ bitmap = bytearray()
64
+ while True:
65
+ s = f.readline()
66
+ if not s or s.startswith(b"ENDCHAR"):
67
+ break
68
+ bitmap += s[:-1]
69
+
70
+ # The word BBX
71
+ # followed by the width in x (BBw), height in y (BBh),
72
+ # and x and y displacement (BBxoff0, BByoff0)
73
+ # of the lower left corner from the origin of the character.
74
+ width, height, x_disp, y_disp = (int(p) for p in props["BBX"].split())
75
+ Image._decompression_bomb_check((width, height))
76
+
77
+ # The word DWIDTH
78
+ # followed by the width in x and y of the character in device pixels.
79
+ dwx, dwy = (int(p) for p in props["DWIDTH"].split())
80
+
81
+ bbox = (
82
+ (dwx, dwy),
83
+ (x_disp, -y_disp - height, width + x_disp, -y_disp),
84
+ (0, 0, width, height),
85
+ )
86
+
87
+ im = Image.frombytes("1", (width, height), bitmap, "hex", "1")
88
+
89
+ return id, int(props["ENCODING"]), bbox, im
90
+
91
+
92
+ class BdfFontFile(FontFile.FontFile):
93
+ """Font file plugin for the X11 BDF format."""
94
+
95
+ def __init__(self, fp: BinaryIO) -> None:
96
+ super().__init__()
97
+
98
+ s = fp.readline()
99
+ if not s.startswith(b"STARTFONT 2.1"):
100
+ msg = "not a valid BDF file"
101
+ raise SyntaxError(msg)
102
+
103
+ props = {}
104
+ comments = []
105
+
106
+ while True:
107
+ s = fp.readline()
108
+ if not s or s.startswith(b"ENDPROPERTIES"):
109
+ break
110
+ i = s.find(b" ")
111
+ props[s[:i].decode("ascii")] = s[i + 1 : -1].decode("ascii")
112
+ if s[:i] in [b"COMMENT", b"COPYRIGHT"]:
113
+ if s.find(b"LogicalFontDescription") < 0:
114
+ comments.append(s[i + 1 : -1].decode("ascii"))
115
+
116
+ while True:
117
+ c = bdf_char(fp)
118
+ if not c:
119
+ break
120
+ id, ch, (xy, dst, src), im = c
121
+ if 0 <= ch < len(self.glyph):
122
+ self.glyph[ch] = xy, dst, src, im
hug/lib/python3.12/site-packages/PIL/BlpImagePlugin.py ADDED
@@ -0,0 +1,503 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Blizzard Mipmap Format (.blp)
3
+ Jerome Leclanche <jerome@leclan.ch>
4
+
5
+ The contents of this file are hereby released in the public domain (CC0)
6
+ Full text of the CC0 license:
7
+ https://creativecommons.org/publicdomain/zero/1.0/
8
+
9
+ BLP1 files, used mostly in Warcraft III, are not fully supported.
10
+ All types of BLP2 files used in World of Warcraft are supported.
11
+
12
+ The BLP file structure consists of a header, up to 16 mipmaps of the
13
+ texture
14
+
15
+ Texture sizes must be powers of two, though the two dimensions do
16
+ not have to be equal; 512x256 is valid, but 512x200 is not.
17
+ The first mipmap (mipmap #0) is the full size image; each subsequent
18
+ mipmap halves both dimensions. The final mipmap should be 1x1.
19
+
20
+ BLP files come in many different flavours:
21
+ * JPEG-compressed (type == 0) - only supported for BLP1.
22
+ * RAW images (type == 1, encoding == 1). Each mipmap is stored as an
23
+ array of 8-bit values, one per pixel, left to right, top to bottom.
24
+ Each value is an index to the palette.
25
+ * DXT-compressed (type == 1, encoding == 2):
26
+ - DXT1 compression is used if alpha_encoding == 0.
27
+ - An additional alpha bit is used if alpha_depth == 1.
28
+ - DXT3 compression is used if alpha_encoding == 1.
29
+ - DXT5 compression is used if alpha_encoding == 7.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ __lazy_modules__ = {"io", "struct"}
35
+
36
+ import abc
37
+ import os
38
+ import struct
39
+ from enum import IntEnum
40
+ from io import BytesIO
41
+
42
+ from . import Image, ImageFile
43
+
44
+ TYPE_CHECKING = False
45
+ if TYPE_CHECKING:
46
+ from typing import IO
47
+
48
+
49
+ class Format(IntEnum):
50
+ JPEG = 0
51
+
52
+
53
+ class Encoding(IntEnum):
54
+ UNCOMPRESSED = 1
55
+ DXT = 2
56
+ UNCOMPRESSED_RAW_BGRA = 3
57
+
58
+
59
+ class AlphaEncoding(IntEnum):
60
+ DXT1 = 0
61
+ DXT3 = 1
62
+ DXT5 = 7
63
+
64
+
65
+ def unpack_565(i: int) -> tuple[int, int, int]:
66
+ return ((i >> 11) & 0x1F) << 3, ((i >> 5) & 0x3F) << 2, (i & 0x1F) << 3
67
+
68
+
69
+ def decode_dxt1(
70
+ data: bytes, alpha: bool = False
71
+ ) -> tuple[bytearray, bytearray, bytearray, bytearray]:
72
+ """
73
+ input: one "row" of data (i.e. will produce 4*width pixels)
74
+ """
75
+
76
+ blocks = len(data) // 8 # number of blocks in row
77
+ ret = (bytearray(), bytearray(), bytearray(), bytearray())
78
+
79
+ for block_index in range(blocks):
80
+ # Decode next 8-byte block.
81
+ idx = block_index * 8
82
+ color0, color1, bits = struct.unpack_from("<HHI", data, idx)
83
+
84
+ r0, g0, b0 = unpack_565(color0)
85
+ r1, g1, b1 = unpack_565(color1)
86
+
87
+ # Decode this block into 4x4 pixels
88
+ # Accumulate the results onto our 4 row accumulators
89
+ for j in range(4):
90
+ for i in range(4):
91
+ # get next control op and generate a pixel
92
+
93
+ control = bits & 3
94
+ bits = bits >> 2
95
+
96
+ a = 0xFF
97
+ if control == 0:
98
+ r, g, b = r0, g0, b0
99
+ elif control == 1:
100
+ r, g, b = r1, g1, b1
101
+ elif control == 2:
102
+ if color0 > color1:
103
+ r = (2 * r0 + r1) // 3
104
+ g = (2 * g0 + g1) // 3
105
+ b = (2 * b0 + b1) // 3
106
+ else:
107
+ r = (r0 + r1) // 2
108
+ g = (g0 + g1) // 2
109
+ b = (b0 + b1) // 2
110
+ elif control == 3:
111
+ if color0 > color1:
112
+ r = (2 * r1 + r0) // 3
113
+ g = (2 * g1 + g0) // 3
114
+ b = (2 * b1 + b0) // 3
115
+ else:
116
+ r, g, b, a = 0, 0, 0, 0
117
+
118
+ if alpha:
119
+ ret[j].extend([r, g, b, a])
120
+ else:
121
+ ret[j].extend([r, g, b])
122
+
123
+ return ret
124
+
125
+
126
+ def decode_dxt3(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]:
127
+ """
128
+ input: one "row" of data (i.e. will produce 4*width pixels)
129
+ """
130
+
131
+ blocks = len(data) // 16 # number of blocks in row
132
+ ret = (bytearray(), bytearray(), bytearray(), bytearray())
133
+
134
+ for block_index in range(blocks):
135
+ idx = block_index * 16
136
+ block = data[idx : idx + 16]
137
+ # Decode next 16-byte block.
138
+ bits = struct.unpack_from("<8B", block)
139
+ color0, color1 = struct.unpack_from("<HH", block, 8)
140
+
141
+ (code,) = struct.unpack_from("<I", block, 12)
142
+
143
+ r0, g0, b0 = unpack_565(color0)
144
+ r1, g1, b1 = unpack_565(color1)
145
+
146
+ for j in range(4):
147
+ high = False # Do we want the higher bits?
148
+ for i in range(4):
149
+ alphacode_index = (4 * j + i) // 2
150
+ a = bits[alphacode_index]
151
+ if high:
152
+ high = False
153
+ a >>= 4
154
+ else:
155
+ high = True
156
+ a &= 0xF
157
+ a *= 17 # We get a value between 0 and 15
158
+
159
+ color_code = (code >> 2 * (4 * j + i)) & 0x03
160
+
161
+ if color_code == 0:
162
+ r, g, b = r0, g0, b0
163
+ elif color_code == 1:
164
+ r, g, b = r1, g1, b1
165
+ elif color_code == 2:
166
+ r = (2 * r0 + r1) // 3
167
+ g = (2 * g0 + g1) // 3
168
+ b = (2 * b0 + b1) // 3
169
+ elif color_code == 3:
170
+ r = (2 * r1 + r0) // 3
171
+ g = (2 * g1 + g0) // 3
172
+ b = (2 * b1 + b0) // 3
173
+
174
+ ret[j].extend([r, g, b, a])
175
+
176
+ return ret
177
+
178
+
179
+ def decode_dxt5(data: bytes) -> tuple[bytearray, bytearray, bytearray, bytearray]:
180
+ """
181
+ input: one "row" of data (i.e. will produce 4 * width pixels)
182
+ """
183
+
184
+ blocks = len(data) // 16 # number of blocks in row
185
+ ret = (bytearray(), bytearray(), bytearray(), bytearray())
186
+
187
+ for block_index in range(blocks):
188
+ idx = block_index * 16
189
+ block = data[idx : idx + 16]
190
+ # Decode next 16-byte block.
191
+ a0, a1 = struct.unpack_from("<BB", block)
192
+
193
+ bits = struct.unpack_from("<6B", block, 2)
194
+ alphacode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
195
+ alphacode2 = bits[0] | (bits[1] << 8)
196
+
197
+ color0, color1 = struct.unpack_from("<HH", block, 8)
198
+
199
+ (code,) = struct.unpack_from("<I", block, 12)
200
+
201
+ r0, g0, b0 = unpack_565(color0)
202
+ r1, g1, b1 = unpack_565(color1)
203
+
204
+ for j in range(4):
205
+ for i in range(4):
206
+ # get next control op and generate a pixel
207
+ alphacode_index = 3 * (4 * j + i)
208
+
209
+ if alphacode_index <= 12:
210
+ alphacode = (alphacode2 >> alphacode_index) & 0x07
211
+ elif alphacode_index == 15:
212
+ alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06)
213
+ else: # alphacode_index >= 18 and alphacode_index <= 45
214
+ alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07
215
+
216
+ if alphacode == 0:
217
+ a = a0
218
+ elif alphacode == 1:
219
+ a = a1
220
+ elif a0 > a1:
221
+ a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7
222
+ elif alphacode == 6:
223
+ a = 0
224
+ elif alphacode == 7:
225
+ a = 255
226
+ else:
227
+ a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5
228
+
229
+ color_code = (code >> 2 * (4 * j + i)) & 0x03
230
+
231
+ if color_code == 0:
232
+ r, g, b = r0, g0, b0
233
+ elif color_code == 1:
234
+ r, g, b = r1, g1, b1
235
+ elif color_code == 2:
236
+ r = (2 * r0 + r1) // 3
237
+ g = (2 * g0 + g1) // 3
238
+ b = (2 * b0 + b1) // 3
239
+ elif color_code == 3:
240
+ r = (2 * r1 + r0) // 3
241
+ g = (2 * g1 + g0) // 3
242
+ b = (2 * b1 + b0) // 3
243
+
244
+ ret[j].extend([r, g, b, a])
245
+
246
+ return ret
247
+
248
+
249
+ class BLPFormatError(NotImplementedError):
250
+ pass
251
+
252
+
253
+ def _accept(prefix: bytes) -> bool:
254
+ return prefix.startswith((b"BLP1", b"BLP2"))
255
+
256
+
257
+ class BlpImageFile(ImageFile.ImageFile):
258
+ """
259
+ Blizzard Mipmap Format
260
+ """
261
+
262
+ format = "BLP"
263
+ format_description = "Blizzard Mipmap Format"
264
+
265
+ def _open(self) -> None:
266
+ assert self.fp is not None
267
+ self.magic = self.fp.read(4)
268
+ if not _accept(self.magic):
269
+ msg = f"Bad BLP magic {repr(self.magic)}"
270
+ raise BLPFormatError(msg)
271
+
272
+ compression = struct.unpack("<i", self.fp.read(4))[0]
273
+ if self.magic == b"BLP1":
274
+ alpha = struct.unpack("<I", self.fp.read(4))[0] != 0
275
+ else:
276
+ encoding = struct.unpack("<b", self.fp.read(1))[0]
277
+ alpha = struct.unpack("<b", self.fp.read(1))[0] != 0
278
+ alpha_encoding = struct.unpack("<b", self.fp.read(1))[0]
279
+ self.fp.seek(1, os.SEEK_CUR) # mips
280
+
281
+ self._size = struct.unpack("<II", self.fp.read(8))
282
+
283
+ args: tuple[int, int, bool] | tuple[int, int, bool, int]
284
+ if self.magic == b"BLP1":
285
+ encoding = struct.unpack("<i", self.fp.read(4))[0]
286
+ self.fp.seek(4, os.SEEK_CUR) # subtype
287
+
288
+ args = (compression, encoding, alpha)
289
+ offset = 28
290
+ else:
291
+ args = (compression, encoding, alpha, alpha_encoding)
292
+ offset = 20
293
+
294
+ decoder = self.magic.decode()
295
+
296
+ self._mode = "RGBA" if alpha else "RGB"
297
+ self.tile = [ImageFile._Tile(decoder, (0, 0, *self.size), offset, args)]
298
+
299
+
300
+ class _BLPBaseDecoder(abc.ABC, ImageFile.PyDecoder):
301
+ _pulls_fd = True
302
+
303
+ def decode(self, buffer: Image.DecoderInput) -> tuple[int, int]:
304
+ try:
305
+ self._read_header()
306
+ self._load()
307
+ except struct.error as e:
308
+ msg = "Truncated BLP file"
309
+ raise OSError(msg) from e
310
+ return -1, 0
311
+
312
+ @abc.abstractmethod
313
+ def _load(self) -> None:
314
+ pass
315
+
316
+ def _read_header(self) -> None:
317
+ self._offsets = struct.unpack("<16I", self._safe_read(16 * 4))
318
+ self._lengths = struct.unpack("<16I", self._safe_read(16 * 4))
319
+
320
+ def _safe_read(self, length: int) -> bytes:
321
+ assert self.fd is not None
322
+ return ImageFile._safe_read(self.fd, length)
323
+
324
+ def _read_palette(self) -> list[tuple[int, int, int, int]]:
325
+ ret = []
326
+ for i in range(256):
327
+ try:
328
+ b, g, r, a = struct.unpack("<4B", self._safe_read(4))
329
+ except struct.error:
330
+ break
331
+ ret.append((b, g, r, a))
332
+ return ret
333
+
334
+ def _read_bgra(
335
+ self, palette: list[tuple[int, int, int, int]], alpha: bool
336
+ ) -> bytearray:
337
+ data = bytearray()
338
+ _data = BytesIO(self._safe_read(self._lengths[0]))
339
+ while True:
340
+ try:
341
+ (offset,) = struct.unpack("<B", _data.read(1))
342
+ except struct.error:
343
+ break
344
+ b, g, r, a = palette[offset]
345
+ d: tuple[int, ...] = (r, g, b)
346
+ if alpha:
347
+ d += (a,)
348
+ data.extend(d)
349
+ return data
350
+
351
+
352
+ class BLP1Decoder(_BLPBaseDecoder):
353
+ def _load(self) -> None:
354
+ _compression, _encoding, alpha = self.args
355
+
356
+ if _compression == Format.JPEG:
357
+ self._decode_jpeg_stream()
358
+
359
+ elif _compression == 1:
360
+ if _encoding in (4, 5):
361
+ palette = self._read_palette()
362
+ data = self._read_bgra(palette, alpha)
363
+ self.set_as_raw(data)
364
+ else:
365
+ msg = f"Unsupported BLP encoding {repr(_encoding)}"
366
+ raise BLPFormatError(msg)
367
+ else:
368
+ msg = f"Unsupported BLP compression {repr(_encoding)}"
369
+ raise BLPFormatError(msg)
370
+
371
+ def _decode_jpeg_stream(self) -> None:
372
+ from .JpegImagePlugin import JpegImageFile
373
+
374
+ (jpeg_header_size,) = struct.unpack("<I", self._safe_read(4))
375
+ jpeg_header = self._safe_read(jpeg_header_size)
376
+ assert self.fd is not None
377
+ self._safe_read(self._offsets[0] - self.fd.tell()) # What IS this?
378
+ data = self._safe_read(self._lengths[0])
379
+ data = jpeg_header + data
380
+ image = JpegImageFile(BytesIO(data))
381
+ Image._decompression_bomb_check(image.size)
382
+ if image.mode == "CMYK":
383
+ args = image.tile[0].args
384
+ assert isinstance(args, tuple)
385
+ image.tile = [image.tile[0]._replace(args=(args[0], "CMYK"))]
386
+ self.set_as_raw(image.convert("RGB").tobytes(), "BGR")
387
+
388
+
389
+ class BLP2Decoder(_BLPBaseDecoder):
390
+ def _load(self) -> None:
391
+ _compression, _encoding, alpha, _alpha_encoding = self.args
392
+
393
+ palette = self._read_palette()
394
+
395
+ assert self.fd is not None
396
+ self.fd.seek(self._offsets[0])
397
+
398
+ if _compression == 1:
399
+ # Uncompressed or DirectX compression
400
+
401
+ if _encoding == Encoding.UNCOMPRESSED:
402
+ data = self._read_bgra(palette, alpha)
403
+
404
+ elif _encoding == Encoding.DXT:
405
+ data = bytearray()
406
+ if _alpha_encoding == AlphaEncoding.DXT1:
407
+ linesize = (self.state.xsize + 3) // 4 * 8
408
+ for yb in range((self.state.ysize + 3) // 4):
409
+ for d in decode_dxt1(self._safe_read(linesize), alpha):
410
+ data += d
411
+
412
+ elif _alpha_encoding == AlphaEncoding.DXT3:
413
+ linesize = (self.state.xsize + 3) // 4 * 16
414
+ for yb in range((self.state.ysize + 3) // 4):
415
+ for d in decode_dxt3(self._safe_read(linesize)):
416
+ data += d
417
+
418
+ elif _alpha_encoding == AlphaEncoding.DXT5:
419
+ linesize = (self.state.xsize + 3) // 4 * 16
420
+ for yb in range((self.state.ysize + 3) // 4):
421
+ for d in decode_dxt5(self._safe_read(linesize)):
422
+ data += d
423
+ else:
424
+ msg = f"Unsupported alpha encoding {repr(_alpha_encoding)}"
425
+ raise BLPFormatError(msg)
426
+ else:
427
+ msg = f"Unknown BLP encoding {repr(_encoding)}"
428
+ raise BLPFormatError(msg)
429
+
430
+ else:
431
+ msg = f"Unknown BLP compression {repr(_compression)}"
432
+ raise BLPFormatError(msg)
433
+
434
+ self.set_as_raw(data)
435
+
436
+
437
+ class BLPEncoder(ImageFile.PyEncoder):
438
+ _pushes_fd = True
439
+
440
+ def _write_palette(self) -> bytes:
441
+ data = b""
442
+ assert self.im is not None
443
+ palette = self.im.getpalette("RGBA")
444
+ for i in range(len(palette) // 4):
445
+ r, g, b, a = palette[i * 4 : (i + 1) * 4]
446
+ data += struct.pack("<4B", b, g, r, a)
447
+ while len(data) < 256 * 4:
448
+ data += b"\x00" * 4
449
+ return data
450
+
451
+ def encode(self, bufsize: int) -> tuple[int, int, bytes]:
452
+ palette_data = self._write_palette()
453
+
454
+ offset = 20 + 16 * 4 * 2 + len(palette_data)
455
+ data = struct.pack("<16I", offset, *((0,) * 15))
456
+
457
+ assert self.im is not None
458
+ w, h = self.im.size
459
+ data += struct.pack("<16I", w * h, *((0,) * 15))
460
+
461
+ data += palette_data
462
+
463
+ for y in range(h):
464
+ for x in range(w):
465
+ data += struct.pack("<B", self.im.getpixel((x, y)))
466
+
467
+ return len(data), 0, data
468
+
469
+
470
+ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
471
+ if im.mode != "P":
472
+ msg = "Unsupported BLP image mode"
473
+ raise ValueError(msg)
474
+
475
+ magic = b"BLP1" if im.encoderinfo.get("blp_version") == "BLP1" else b"BLP2"
476
+ fp.write(magic)
477
+
478
+ assert im.palette is not None
479
+ fp.write(struct.pack("<i", 1)) # Uncompressed or DirectX compression
480
+
481
+ alpha_depth = 1 if im.palette.mode == "RGBA" else 0
482
+ if magic == b"BLP1":
483
+ fp.write(struct.pack("<L", alpha_depth))
484
+ else:
485
+ fp.write(struct.pack("<b", Encoding.UNCOMPRESSED))
486
+ fp.write(struct.pack("<b", alpha_depth))
487
+ fp.write(struct.pack("<b", 0)) # alpha encoding
488
+ fp.write(struct.pack("<b", 0)) # mips
489
+ fp.write(struct.pack("<II", *im.size))
490
+ if magic == b"BLP1":
491
+ fp.write(struct.pack("<i", 5))
492
+ fp.write(struct.pack("<i", 0))
493
+
494
+ ImageFile._save(im, fp, [ImageFile._Tile("BLP", (0, 0, *im.size), 0, im.mode)])
495
+
496
+
497
+ Image.register_open(BlpImageFile.format, BlpImageFile, _accept)
498
+ Image.register_extension(BlpImageFile.format, ".blp")
499
+ Image.register_decoder("BLP1", BLP1Decoder)
500
+ Image.register_decoder("BLP2", BLP2Decoder)
501
+
502
+ Image.register_save(BlpImageFile.format, _save)
503
+ Image.register_encoder("BLP", BLPEncoder)
hug/lib/python3.12/site-packages/PIL/BmpImagePlugin.py ADDED
@@ -0,0 +1,526 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # BMP file handler
6
+ #
7
+ # Windows (and OS/2) native bitmap storage format.
8
+ #
9
+ # history:
10
+ # 1995-09-01 fl Created
11
+ # 1996-04-30 fl Added save
12
+ # 1997-08-27 fl Fixed save of 1-bit images
13
+ # 1998-03-06 fl Load P images as L where possible
14
+ # 1998-07-03 fl Load P images as 1 where possible
15
+ # 1998-12-29 fl Handle small palettes
16
+ # 2002-12-30 fl Fixed load of 1-bit palette images
17
+ # 2003-04-21 fl Fixed load of 1-bit monochrome images
18
+ # 2003-04-23 fl Added limited support for BI_BITFIELDS compression
19
+ #
20
+ # Copyright (c) 1997-2003 by Secret Labs AB
21
+ # Copyright (c) 1995-2003 by Fredrik Lundh
22
+ #
23
+ # See the README file for information on usage and redistribution.
24
+ #
25
+ from __future__ import annotations
26
+
27
+ __lazy_modules__ = {"PIL._binary"}
28
+
29
+ import os
30
+
31
+ from . import Image, ImageFile, ImagePalette
32
+ from ._binary import i16le as i16
33
+ from ._binary import i32le as i32
34
+ from ._binary import o8
35
+ from ._binary import o16le as o16
36
+ from ._binary import o32le as o32
37
+
38
+ TYPE_CHECKING = False
39
+ if TYPE_CHECKING:
40
+ from typing import IO, Any
41
+
42
+ #
43
+ # --------------------------------------------------------------------
44
+ # Read BMP file
45
+
46
+ BIT2MODE = {
47
+ # bits => mode, rawmode
48
+ 1: ("P", "P;1"),
49
+ 4: ("P", "P;4"),
50
+ 8: ("P", "P"),
51
+ 16: ("RGB", "BGR;15"),
52
+ 24: ("RGB", "BGR"),
53
+ 32: ("RGB", "BGRX"),
54
+ }
55
+
56
+ USE_RAW_ALPHA = False
57
+
58
+
59
+ def _accept(prefix: bytes) -> bool:
60
+ return prefix.startswith(b"BM")
61
+
62
+
63
+ def _dib_accept(prefix: bytes) -> bool:
64
+ return i32(prefix) in [12, 40, 52, 56, 64, 108, 124]
65
+
66
+
67
+ # =============================================================================
68
+ # Image plugin for the Windows BMP format.
69
+ # =============================================================================
70
+ class BmpImageFile(ImageFile.ImageFile):
71
+ """Image plugin for the Windows Bitmap format (BMP)"""
72
+
73
+ # ------------------------------------------------------------- Description
74
+ format_description = "Windows Bitmap"
75
+ format = "BMP"
76
+
77
+ # -------------------------------------------------- BMP Compression values
78
+ COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5}
79
+ for k, v in COMPRESSIONS.items():
80
+ vars()[k] = v
81
+
82
+ def _bitmap(self, header: int = 0, offset: int = 0) -> None:
83
+ """Read relevant info about the BMP"""
84
+ assert self.fp is not None
85
+ read, seek = self.fp.read, self.fp.seek
86
+ if header:
87
+ seek(header)
88
+ # read bmp header size @offset 14 (this is part of the header size)
89
+ file_info: dict[str, bool | int | tuple[int, ...]] = {
90
+ "header_size": i32(read(4)),
91
+ "direction": -1,
92
+ }
93
+
94
+ # -------------------- If requested, read header at a specific position
95
+ # read the rest of the bmp header, without its size
96
+ assert isinstance(file_info["header_size"], int)
97
+ header_data = ImageFile._safe_read(self.fp, file_info["header_size"] - 4)
98
+
99
+ # ------------------------------- Windows Bitmap v2, IBM OS/2 Bitmap v1
100
+ # ----- This format has different offsets because of width/height types
101
+ # 12: BITMAPCOREHEADER/OS21XBITMAPHEADER
102
+ if file_info["header_size"] == 12:
103
+ file_info["width"] = i16(header_data, 0)
104
+ file_info["height"] = i16(header_data, 2)
105
+ file_info["planes"] = i16(header_data, 4)
106
+ file_info["bits"] = i16(header_data, 6)
107
+ file_info["compression"] = self.COMPRESSIONS["RAW"]
108
+ file_info["palette_padding"] = 3
109
+
110
+ # --------------------------------------------- Windows Bitmap v3 to v5
111
+ # 40: BITMAPINFOHEADER
112
+ # 52: BITMAPV2HEADER
113
+ # 56: BITMAPV3HEADER
114
+ # 64: BITMAPCOREHEADER2/OS22XBITMAPHEADER
115
+ # 108: BITMAPV4HEADER
116
+ # 124: BITMAPV5HEADER
117
+ elif file_info["header_size"] in (40, 52, 56, 64, 108, 124):
118
+ file_info["y_flip"] = header_data[7] == 0xFF
119
+ file_info["direction"] = 1 if file_info["y_flip"] else -1
120
+ file_info["width"] = i32(header_data, 0)
121
+ file_info["height"] = (
122
+ i32(header_data, 4)
123
+ if not file_info["y_flip"]
124
+ else 2**32 - i32(header_data, 4)
125
+ )
126
+ file_info["planes"] = i16(header_data, 8)
127
+ file_info["bits"] = i16(header_data, 10)
128
+ file_info["compression"] = i32(header_data, 12)
129
+ # byte size of pixel data
130
+ file_info["data_size"] = i32(header_data, 16)
131
+ file_info["pixels_per_meter"] = (
132
+ i32(header_data, 20),
133
+ i32(header_data, 24),
134
+ )
135
+ file_info["colors"] = i32(header_data, 28)
136
+ file_info["palette_padding"] = 4
137
+ assert isinstance(file_info["pixels_per_meter"], tuple)
138
+ dpi = tuple(x / 39.3701 for x in file_info["pixels_per_meter"])
139
+ if dpi != (0, 0):
140
+ self.info["dpi"] = dpi
141
+ if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]:
142
+ masks = ["r_mask", "g_mask", "b_mask"]
143
+ if len(header_data) >= 48:
144
+ if len(header_data) >= 52:
145
+ masks.append("a_mask")
146
+ else:
147
+ file_info["a_mask"] = 0x0
148
+ for idx, mask in enumerate(masks):
149
+ file_info[mask] = i32(header_data, 36 + idx * 4)
150
+ else:
151
+ # 40 byte headers only have the three components in the
152
+ # bitfields masks, ref:
153
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
154
+ # See also
155
+ # https://github.com/python-pillow/Pillow/issues/1293
156
+ # There is a 4th component in the RGBQuad, in the alpha
157
+ # location, but it is listed as a reserved component,
158
+ # and it is not generally an alpha channel
159
+ file_info["a_mask"] = 0x0
160
+ for mask in masks:
161
+ file_info[mask] = i32(read(4))
162
+ assert isinstance(file_info["r_mask"], int)
163
+ assert isinstance(file_info["g_mask"], int)
164
+ assert isinstance(file_info["b_mask"], int)
165
+ assert isinstance(file_info["a_mask"], int)
166
+ file_info["rgb_mask"] = (
167
+ file_info["r_mask"],
168
+ file_info["g_mask"],
169
+ file_info["b_mask"],
170
+ )
171
+ file_info["rgba_mask"] = (
172
+ file_info["r_mask"],
173
+ file_info["g_mask"],
174
+ file_info["b_mask"],
175
+ file_info["a_mask"],
176
+ )
177
+ else:
178
+ msg = f"Unsupported BMP header type ({file_info['header_size']})"
179
+ raise OSError(msg)
180
+
181
+ # ------------------ Special case : header is reported 40, which
182
+ # ---------------------- is shorter than real size for bpp >= 16
183
+ assert isinstance(file_info["width"], int)
184
+ assert isinstance(file_info["height"], int)
185
+ self._size = file_info["width"], file_info["height"]
186
+
187
+ # ------- If color count was not found in the header, compute from bits
188
+ assert isinstance(file_info["bits"], int)
189
+ if not file_info.get("colors", 0):
190
+ file_info["colors"] = 1 << file_info["bits"]
191
+ assert isinstance(file_info["palette_padding"], int)
192
+ assert isinstance(file_info["colors"], int)
193
+ if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8:
194
+ offset += file_info["palette_padding"] * file_info["colors"]
195
+
196
+ # ---------------------- Check bit depth for unusual unsupported values
197
+ self._mode, raw_mode = BIT2MODE.get(file_info["bits"], ("", ""))
198
+ if not self.mode:
199
+ msg = f"Unsupported BMP pixel depth ({file_info['bits']})"
200
+ raise OSError(msg)
201
+
202
+ # ---------------- Process BMP with Bitfields compression (not palette)
203
+ decoder_name = "raw"
204
+ if file_info["compression"] == self.COMPRESSIONS["BITFIELDS"]:
205
+ SUPPORTED: dict[int, list[tuple[int, ...]]] = {
206
+ 32: [
207
+ (0xFF0000, 0xFF00, 0xFF, 0x0),
208
+ (0xFF000000, 0xFF0000, 0xFF00, 0x0),
209
+ (0xFF000000, 0xFF00, 0xFF, 0x0),
210
+ (0xFF000000, 0xFF0000, 0xFF00, 0xFF),
211
+ (0xFF, 0xFF00, 0xFF0000, 0xFF000000),
212
+ (0xFF0000, 0xFF00, 0xFF, 0xFF000000),
213
+ (0xFF000000, 0xFF00, 0xFF, 0xFF0000),
214
+ (0x0, 0x0, 0x0, 0x0),
215
+ ],
216
+ 24: [(0xFF0000, 0xFF00, 0xFF)],
217
+ 16: [(0xF800, 0x7E0, 0x1F), (0x7C00, 0x3E0, 0x1F)],
218
+ }
219
+ MASK_MODES = {
220
+ (32, (0xFF0000, 0xFF00, 0xFF, 0x0)): "BGRX",
221
+ (32, (0xFF000000, 0xFF0000, 0xFF00, 0x0)): "XBGR",
222
+ (32, (0xFF000000, 0xFF00, 0xFF, 0x0)): "BGXR",
223
+ (32, (0xFF000000, 0xFF0000, 0xFF00, 0xFF)): "ABGR",
224
+ (32, (0xFF, 0xFF00, 0xFF0000, 0xFF000000)): "RGBA",
225
+ (32, (0xFF0000, 0xFF00, 0xFF, 0xFF000000)): "BGRA",
226
+ (32, (0xFF000000, 0xFF00, 0xFF, 0xFF0000)): "BGAR",
227
+ (32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
228
+ (24, (0xFF0000, 0xFF00, 0xFF)): "BGR",
229
+ (16, (0xF800, 0x7E0, 0x1F)): "BGR;16",
230
+ (16, (0x7C00, 0x3E0, 0x1F)): "BGR;15",
231
+ }
232
+ if file_info["bits"] in SUPPORTED:
233
+ if (
234
+ file_info["bits"] == 32
235
+ and file_info["rgba_mask"] in SUPPORTED[file_info["bits"]]
236
+ ):
237
+ assert isinstance(file_info["rgba_mask"], tuple)
238
+ raw_mode = MASK_MODES[(file_info["bits"], file_info["rgba_mask"])]
239
+ self._mode = "RGBA" if "A" in raw_mode else self.mode
240
+ elif (
241
+ file_info["bits"] in (24, 16)
242
+ and file_info["rgb_mask"] in SUPPORTED[file_info["bits"]]
243
+ ):
244
+ assert isinstance(file_info["rgb_mask"], tuple)
245
+ raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
246
+ else:
247
+ msg = "Unsupported BMP bitfields layout"
248
+ raise OSError(msg)
249
+ else:
250
+ msg = "Unsupported BMP bitfields layout"
251
+ raise OSError(msg)
252
+ elif file_info["compression"] == self.COMPRESSIONS["RAW"]:
253
+ if file_info["bits"] == 32 and (
254
+ header == 22 or USE_RAW_ALPHA # 32-bit .cur offset
255
+ ):
256
+ raw_mode, self._mode = "BGRA", "RGBA"
257
+ elif file_info["compression"] in (
258
+ self.COMPRESSIONS["RLE8"],
259
+ self.COMPRESSIONS["RLE4"],
260
+ ):
261
+ decoder_name = "bmp_rle"
262
+ else:
263
+ msg = f"Unsupported BMP compression ({file_info['compression']})"
264
+ raise OSError(msg)
265
+
266
+ # --------------- Once the header is processed, process the palette/LUT
267
+ if self.mode == "P": # Paletted for 1, 4 and 8 bit images
268
+ # ---------------------------------------------------- 1-bit images
269
+ if not (0 < file_info["colors"] <= 65536):
270
+ msg = f"Unsupported BMP Palette size ({file_info['colors']})"
271
+ raise OSError(msg)
272
+ else:
273
+ padding = file_info["palette_padding"]
274
+ palette = read(padding * file_info["colors"])
275
+ grayscale = True
276
+ indices = (
277
+ (0, 255)
278
+ if file_info["colors"] == 2
279
+ else list(range(file_info["colors"]))
280
+ )
281
+
282
+ # ----------------- Check if grayscale and ignore palette if so
283
+ for ind, val in enumerate(indices):
284
+ rgb = palette[ind * padding : ind * padding + 3]
285
+ if rgb != o8(val) * 3:
286
+ grayscale = False
287
+
288
+ # ------- If all colors are gray, white or black, ditch palette
289
+ if grayscale:
290
+ self._mode = "1" if file_info["colors"] <= 2 else "L"
291
+ raw_mode = self.mode
292
+ else:
293
+ self._mode = "P"
294
+ self.palette = ImagePalette.raw(
295
+ "BGRX" if padding == 4 else "BGR", palette
296
+ )
297
+
298
+ # ---------------------------- Finally set the tile data for the plugin
299
+ self.info["compression"] = file_info["compression"]
300
+ args: list[Any] = [raw_mode]
301
+ if decoder_name == "bmp_rle":
302
+ args.append(file_info["compression"] == self.COMPRESSIONS["RLE4"])
303
+ else:
304
+ assert isinstance(file_info["width"], int)
305
+ args.append(((file_info["width"] * file_info["bits"] + 31) >> 3) & (~3))
306
+ args.append(file_info["direction"])
307
+ self.tile = [
308
+ ImageFile._Tile(
309
+ decoder_name,
310
+ (0, 0, file_info["width"], file_info["height"]),
311
+ offset or self.fp.tell(),
312
+ tuple(args),
313
+ )
314
+ ]
315
+
316
+ def _open(self) -> None:
317
+ """Open file, check magic number and read header"""
318
+ # read 14 bytes: magic number, filesize, reserved, header final offset
319
+ assert self.fp is not None
320
+ head_data = self.fp.read(14)
321
+ # choke if the file does not have the required magic bytes
322
+ if not _accept(head_data):
323
+ msg = "Not a BMP file"
324
+ raise SyntaxError(msg)
325
+ # read the start position of the BMP image data (u32)
326
+ offset = i32(head_data, 10)
327
+ # load bitmap information (offset=raster info)
328
+ self._bitmap(offset=offset)
329
+
330
+
331
+ class BmpRleDecoder(ImageFile.PyDecoder):
332
+ _pulls_fd = True
333
+
334
+ def decode(self, buffer: Image.DecoderInput) -> tuple[int, int]:
335
+ assert self.fd is not None
336
+ rle4 = self.args[1]
337
+ data = bytearray()
338
+ x = 0
339
+ dest_length = self.state.xsize * self.state.ysize
340
+ while len(data) < dest_length:
341
+ pixels = self.fd.read(1)
342
+ byte = self.fd.read(1)
343
+ if not pixels or not byte:
344
+ break
345
+ num_pixels = pixels[0]
346
+ if num_pixels:
347
+ # encoded mode
348
+ if x + num_pixels > self.state.xsize:
349
+ # Too much data for row
350
+ num_pixels = max(0, self.state.xsize - x)
351
+ if rle4:
352
+ first_pixel = o8(byte[0] >> 4)
353
+ second_pixel = o8(byte[0] & 0x0F)
354
+ for index in range(num_pixels):
355
+ if index % 2 == 0:
356
+ data += first_pixel
357
+ else:
358
+ data += second_pixel
359
+ else:
360
+ data += byte * num_pixels
361
+ x += num_pixels
362
+ else:
363
+ if byte[0] == 0:
364
+ # end of line
365
+ while len(data) % self.state.xsize != 0:
366
+ data += b"\x00"
367
+ x = 0
368
+ elif byte[0] == 1:
369
+ # end of bitmap
370
+ break
371
+ elif byte[0] == 2:
372
+ # delta
373
+ bytes_read = self.fd.read(2)
374
+ if len(bytes_read) < 2:
375
+ break
376
+ right, up = bytes_read
377
+ data += b"\x00" * min(
378
+ dest_length - len(data), right + up * self.state.xsize
379
+ )
380
+ x = len(data) % self.state.xsize
381
+ else:
382
+ # absolute mode
383
+ data_count = min(dest_length - len(data), byte[0])
384
+ if rle4:
385
+ # 2 pixels per byte, padded up to a whole byte
386
+ byte_count = (data_count + 1) // 2
387
+ bytes_read = self.fd.read(byte_count)
388
+ for i, byte_read in enumerate(bytes_read):
389
+ data += o8(byte_read >> 4)
390
+ if i * 2 + 1 < data_count:
391
+ data += o8(byte_read & 0x0F)
392
+ else:
393
+ byte_count = data_count
394
+ bytes_read = self.fd.read(byte_count)
395
+ data += bytes_read
396
+ if len(bytes_read) < byte_count or len(data) == dest_length:
397
+ break
398
+ x += byte[0]
399
+
400
+ # align to 16-bit word boundary
401
+ if self.fd.tell() % 2 != 0:
402
+ self.fd.seek(1, os.SEEK_CUR)
403
+ rawmode = "L" if self.mode in {"1", "L"} else "P"
404
+ self.set_as_raw(bytes(data), rawmode, (0, self.args[-1]))
405
+ return -1, 0
406
+
407
+
408
+ # =============================================================================
409
+ # Image plugin for the DIB format (BMP alias)
410
+ # =============================================================================
411
+ class DibImageFile(BmpImageFile):
412
+ format = "DIB"
413
+ format_description = "Windows Bitmap"
414
+
415
+ def _open(self) -> None:
416
+ self._bitmap()
417
+
418
+
419
+ #
420
+ # --------------------------------------------------------------------
421
+ # Write BMP file
422
+
423
+
424
+ SAVE = {
425
+ "1": ("1", 1, 2),
426
+ "L": ("L", 8, 256),
427
+ "P": ("P", 8, 256),
428
+ "RGB": ("BGR", 24, 0),
429
+ "RGBA": ("BGRA", 32, 0),
430
+ }
431
+
432
+
433
+ def _dib_save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
434
+ _save(im, fp, filename, False)
435
+
436
+
437
+ def _save(
438
+ im: Image.Image, fp: IO[bytes], filename: str | bytes, bitmap_header: bool = True
439
+ ) -> None:
440
+ try:
441
+ rawmode, bits, colors = SAVE[im.mode]
442
+ except KeyError as e:
443
+ msg = f"cannot write mode {im.mode} as BMP"
444
+ raise OSError(msg) from e
445
+
446
+ info = im.encoderinfo
447
+
448
+ dpi = info.get("dpi", (96, 96))
449
+
450
+ # 1 meter == 39.3701 inches
451
+ ppm = tuple(int(x * 39.3701 + 0.5) for x in dpi)
452
+
453
+ stride = ((im.size[0] * bits + 7) // 8 + 3) & (~3)
454
+ header = 40 # or 64 for OS/2 version 2
455
+ image = stride * im.size[1]
456
+
457
+ if im.mode == "1":
458
+ palette = b"".join(o8(i) * 3 + b"\x00" for i in (0, 255))
459
+ elif im.mode == "L":
460
+ palette = b"".join(o8(i) * 3 + b"\x00" for i in range(256))
461
+ elif im.mode == "P":
462
+ # Colors used should not be zero, as that is treated as the maximum
463
+ palette = im.im.getpalette("RGB", "BGRX") or b"\x00\x00\x00\x00"
464
+ colors = len(palette) // 4
465
+ else:
466
+ palette = None
467
+
468
+ # bitmap header
469
+ if bitmap_header:
470
+ offset = 14 + header + colors * 4
471
+ file_size = offset + image
472
+ if file_size > 2**32 - 1:
473
+ msg = "File size is too large for the BMP format"
474
+ raise ValueError(msg)
475
+ fp.write(
476
+ b"BM" # file type (magic)
477
+ + o32(file_size) # file size
478
+ + o32(0) # reserved
479
+ + o32(offset) # image data offset
480
+ )
481
+
482
+ # bitmap info header
483
+ fp.write(
484
+ o32(header) # info header size
485
+ + o32(im.size[0]) # width
486
+ + o32(im.size[1]) # height
487
+ + o16(1) # planes
488
+ + o16(bits) # depth
489
+ + o32(0) # compression (0=uncompressed)
490
+ + o32(image) # size of bitmap
491
+ + o32(ppm[0]) # resolution
492
+ + o32(ppm[1]) # resolution
493
+ + o32(colors) # colors used
494
+ + o32(colors) # colors important
495
+ )
496
+
497
+ fp.write(b"\0" * (header - 40)) # padding (for OS/2 format)
498
+
499
+ if palette:
500
+ fp.write(palette)
501
+
502
+ ImageFile._save(
503
+ im, fp, [ImageFile._Tile("raw", (0, 0, *im.size), 0, (rawmode, stride, -1))]
504
+ )
505
+
506
+
507
+ #
508
+ # --------------------------------------------------------------------
509
+ # Registry
510
+
511
+
512
+ Image.register_open(BmpImageFile.format, BmpImageFile, _accept)
513
+ Image.register_save(BmpImageFile.format, _save)
514
+
515
+ Image.register_extension(BmpImageFile.format, ".bmp")
516
+
517
+ Image.register_mime(BmpImageFile.format, "image/bmp")
518
+
519
+ Image.register_decoder("bmp_rle", BmpRleDecoder)
520
+
521
+ Image.register_open(DibImageFile.format, DibImageFile, _dib_accept)
522
+ Image.register_save(DibImageFile.format, _dib_save)
523
+
524
+ Image.register_extension(DibImageFile.format, ".dib")
525
+
526
+ Image.register_mime(DibImageFile.format, "image/bmp")
hug/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library
3
+ # $Id$
4
+ #
5
+ # BUFR stub adapter
6
+ #
7
+ # Copyright (c) 1996-2003 by Fredrik Lundh
8
+ #
9
+ # See the README file for information on usage and redistribution.
10
+ #
11
+ from __future__ import annotations
12
+
13
+ import os
14
+
15
+ from . import Image, ImageFile
16
+
17
+ TYPE_CHECKING = False
18
+ if TYPE_CHECKING:
19
+ from typing import IO
20
+
21
+
22
+ def register_handler(handler: ImageFile.StubHandler | None) -> None:
23
+ """
24
+ Install application-specific BUFR image handler.
25
+
26
+ :param handler: Handler object.
27
+ """
28
+ BufrStubImageFile._handler = handler
29
+
30
+
31
+ # --------------------------------------------------------------------
32
+ # Image adapter
33
+
34
+
35
+ def _accept(prefix: bytes) -> bool:
36
+ return prefix.startswith((b"BUFR", b"ZCZC"))
37
+
38
+
39
+ class BufrStubImageFile(ImageFile.StubImageFile):
40
+ format = "BUFR"
41
+ format_description = "BUFR"
42
+
43
+ def _open(self) -> None:
44
+ assert self.fp is not None
45
+ if not _accept(self.fp.read(4)):
46
+ msg = "Not a BUFR file"
47
+ raise SyntaxError(msg)
48
+
49
+ self.fp.seek(-4, os.SEEK_CUR)
50
+
51
+ # make something up
52
+ self._mode = "F"
53
+
54
+
55
+ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
56
+ if BufrStubImageFile._handler is None or not hasattr(
57
+ BufrStubImageFile._handler, "save"
58
+ ):
59
+ msg = "BUFR save handler not installed"
60
+ raise OSError(msg)
61
+ BufrStubImageFile._handler.save(im, fp, filename)
62
+
63
+
64
+ # --------------------------------------------------------------------
65
+ # Registry
66
+
67
+ Image.register_open(BufrStubImageFile.format, BufrStubImageFile, _accept)
68
+ Image.register_save(BufrStubImageFile.format, _save)
69
+
70
+ Image.register_extension(BufrStubImageFile.format, ".bufr")
hug/lib/python3.12/site-packages/PIL/ContainerIO.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # a class to read from a container file
6
+ #
7
+ # History:
8
+ # 1995-06-18 fl Created
9
+ # 1995-09-07 fl Added readline(), readlines()
10
+ #
11
+ # Copyright (c) 1997-2001 by Secret Labs AB
12
+ # Copyright (c) 1995 by Fredrik Lundh
13
+ #
14
+ # See the README file for information on usage and redistribution.
15
+ #
16
+ from __future__ import annotations
17
+
18
+ import io
19
+ from typing import IO, AnyStr
20
+
21
+ TYPE_CHECKING = False
22
+ if TYPE_CHECKING:
23
+ from collections.abc import Iterable
24
+ from typing import NoReturn, Self
25
+
26
+
27
+ class ContainerIO(IO[AnyStr]):
28
+ """
29
+ A file object that provides read access to a part of an existing
30
+ file (for example a TAR file).
31
+ """
32
+
33
+ def __init__(self, file: IO[AnyStr], offset: int, length: int) -> None:
34
+ """
35
+ Create file object.
36
+
37
+ :param file: Existing file.
38
+ :param offset: Start of region, in bytes.
39
+ :param length: Size of region, in bytes.
40
+ """
41
+ self.fh: IO[AnyStr] = file
42
+ self.pos = 0
43
+ self.offset = offset
44
+ self.length = length
45
+ self.fh.seek(offset)
46
+
47
+ ##
48
+ # Always false.
49
+
50
+ def isatty(self) -> bool:
51
+ return False
52
+
53
+ def seekable(self) -> bool:
54
+ return True
55
+
56
+ def seek(self, offset: int, mode: int = io.SEEK_SET) -> int:
57
+ """
58
+ Move file pointer.
59
+
60
+ :param offset: Offset in bytes.
61
+ :param mode: Starting position. Use 0 for beginning of region, 1
62
+ for current offset, and 2 for end of region. You cannot move
63
+ the pointer outside the defined region.
64
+ :returns: Offset from start of region, in bytes.
65
+ """
66
+ if mode == 1:
67
+ self.pos = self.pos + offset
68
+ elif mode == 2:
69
+ self.pos = self.length + offset
70
+ else:
71
+ self.pos = offset
72
+ # clamp
73
+ self.pos = max(0, min(self.pos, self.length))
74
+ self.fh.seek(self.offset + self.pos)
75
+ return self.pos
76
+
77
+ def tell(self) -> int:
78
+ """
79
+ Get current file pointer.
80
+
81
+ :returns: Offset from start of region, in bytes.
82
+ """
83
+ return self.pos
84
+
85
+ def readable(self) -> bool:
86
+ return True
87
+
88
+ def read(self, n: int = -1) -> AnyStr:
89
+ """
90
+ Read data.
91
+
92
+ :param n: Number of bytes to read. If omitted, zero or negative,
93
+ read until end of region.
94
+ :returns: An 8-bit string.
95
+ """
96
+ if n > 0:
97
+ n = min(n, self.length - self.pos)
98
+ else:
99
+ n = self.length - self.pos
100
+ if n <= 0: # EOF
101
+ return b"" if "b" in self.fh.mode else "" # type: ignore[return-value]
102
+ self.pos = self.pos + n
103
+ return self.fh.read(n)
104
+
105
+ def readline(self, n: int = -1) -> AnyStr:
106
+ """
107
+ Read a line of text.
108
+
109
+ :param n: Number of bytes to read. If omitted, zero or negative,
110
+ read until end of line.
111
+ :returns: An 8-bit string.
112
+ """
113
+ s: AnyStr = b"" if "b" in self.fh.mode else "" # type: ignore[assignment]
114
+ newline_character = b"\n" if "b" in self.fh.mode else "\n"
115
+ while True:
116
+ c = self.read(1)
117
+ if not c:
118
+ break
119
+ s = s + c
120
+ if c == newline_character or len(s) == n:
121
+ break
122
+ return s
123
+
124
+ def readlines(self, n: int | None = -1) -> list[AnyStr]:
125
+ """
126
+ Read multiple lines of text.
127
+
128
+ :param n: Number of lines to read. If omitted, zero, negative or None,
129
+ read until end of region.
130
+ :returns: A list of 8-bit strings.
131
+ """
132
+ lines = []
133
+ while True:
134
+ s = self.readline()
135
+ if not s:
136
+ break
137
+ lines.append(s)
138
+ if len(lines) == n:
139
+ break
140
+ return lines
141
+
142
+ def writable(self) -> bool:
143
+ return False
144
+
145
+ def write(self, b: AnyStr) -> NoReturn:
146
+ raise NotImplementedError()
147
+
148
+ def writelines(self, lines: Iterable[AnyStr]) -> NoReturn:
149
+ raise NotImplementedError()
150
+
151
+ def truncate(self, size: int | None = None) -> int:
152
+ raise NotImplementedError()
153
+
154
+ def __enter__(self) -> Self:
155
+ return self
156
+
157
+ def __exit__(self, *args: object) -> None:
158
+ self.close()
159
+
160
+ def __iter__(self) -> Self:
161
+ return self
162
+
163
+ def __next__(self) -> AnyStr:
164
+ line = self.readline()
165
+ if not line:
166
+ msg = "end of region"
167
+ raise StopIteration(msg)
168
+ return line
169
+
170
+ def fileno(self) -> int:
171
+ return self.fh.fileno()
172
+
173
+ def flush(self) -> None:
174
+ self.fh.flush()
175
+
176
+ def close(self) -> None:
177
+ self.fh.close()
hug/lib/python3.12/site-packages/PIL/CurImagePlugin.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # Windows Cursor support for PIL
6
+ #
7
+ # notes:
8
+ # uses BmpImagePlugin.py to read the bitmap data.
9
+ #
10
+ # history:
11
+ # 96-05-27 fl Created
12
+ #
13
+ # Copyright (c) Secret Labs AB 1997.
14
+ # Copyright (c) Fredrik Lundh 1996.
15
+ #
16
+ # See the README file for information on usage and redistribution.
17
+ #
18
+ from __future__ import annotations
19
+
20
+ __lazy_modules__ = {"PIL._binary"}
21
+
22
+ from . import BmpImagePlugin, Image, ImageOps
23
+ from ._binary import i16le as i16
24
+ from ._binary import i32le as i32
25
+
26
+ #
27
+ # --------------------------------------------------------------------
28
+
29
+
30
+ def _accept(prefix: bytes) -> bool:
31
+ return prefix.startswith(b"\0\0\2\0")
32
+
33
+
34
+ ##
35
+ # Image plugin for Windows Cursor files.
36
+
37
+
38
+ class CurImageFile(BmpImagePlugin.BmpImageFile):
39
+ format = "CUR"
40
+ format_description = "Windows Cursor"
41
+
42
+ def _open(self) -> None:
43
+ assert self.fp is not None
44
+ offset = self.fp.tell()
45
+
46
+ # check magic
47
+ s = self.fp.read(6)
48
+ if not _accept(s):
49
+ msg = "not a CUR file"
50
+ raise SyntaxError(msg)
51
+
52
+ # pick the largest cursor in the file
53
+ m = b""
54
+ for i in range(i16(s, 4)):
55
+ s = self.fp.read(16)
56
+ if not m:
57
+ m = s
58
+ elif s[0] > m[0] and s[1] > m[1]:
59
+ m = s
60
+ if not m:
61
+ msg = "No cursors were found"
62
+ raise TypeError(msg)
63
+
64
+ # load as bitmap
65
+ self._bitmap(i32(m, 12) + offset)
66
+ self._masked = self.mode in ("1", "L")
67
+ if self._masked:
68
+ self._rawmode = self.mode
69
+ self._mode = "LA"
70
+
71
+ # patch up the bitmap height
72
+ self._size = self.width, self.height // 2
73
+ if not self._masked:
74
+ self.tile = [self.tile[0]._replace(extents=(0, 0, *self.size))]
75
+
76
+ def load_prepare(self) -> None:
77
+ if self._masked:
78
+ self._mode = self._rawmode
79
+ self._size = self.width, self.height * 2
80
+ super().load_prepare()
81
+
82
+ def load_end(self) -> None:
83
+ if not self._masked:
84
+ return
85
+ self._mode = "LA"
86
+ new_height = self.height // 2
87
+
88
+ and_mask = self.im.crop((0, 0, self.width, new_height))
89
+ xor_mask = self.im.crop((0, new_height, self.width, self.height))
90
+
91
+ self._size = self.width, new_height
92
+ self._im = Image.core.fill(self.mode, self.size)
93
+ self._im.paste(
94
+ xor_mask.convert(self.mode),
95
+ (0, 0, *self.size),
96
+ ImageOps.invert(Image.Image()._new(and_mask)).im,
97
+ )
98
+
99
+
100
+ #
101
+ # --------------------------------------------------------------------
102
+
103
+ Image.register_open(CurImageFile.format, CurImageFile, _accept)
104
+
105
+ Image.register_extension(CurImageFile.format, ".cur")
hug/lib/python3.12/site-packages/PIL/DcxImagePlugin.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # DCX file handling
6
+ #
7
+ # DCX is a container file format defined by Intel, commonly used
8
+ # for fax applications. Each DCX file consists of a directory
9
+ # (a list of file offsets) followed by a set of (usually 1-bit)
10
+ # PCX files.
11
+ #
12
+ # History:
13
+ # 1995-09-09 fl Created
14
+ # 1996-03-20 fl Properly derived from PcxImageFile.
15
+ # 1998-07-15 fl Renamed offset attribute to avoid name clash
16
+ # 2002-07-30 fl Fixed file handling
17
+ #
18
+ # Copyright (c) 1997-98 by Secret Labs AB.
19
+ # Copyright (c) 1995-96 by Fredrik Lundh.
20
+ #
21
+ # See the README file for information on usage and redistribution.
22
+ #
23
+ from __future__ import annotations
24
+
25
+ __lazy_modules__ = {"PIL._binary", "PIL._util"}
26
+
27
+ from . import Image
28
+ from ._binary import i32le as i32
29
+ from ._util import DeferredError
30
+ from .PcxImagePlugin import PcxImageFile
31
+
32
+ MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
33
+
34
+
35
+ def _accept(prefix: bytes) -> bool:
36
+ return len(prefix) >= 4 and i32(prefix) == MAGIC
37
+
38
+
39
+ ##
40
+ # Image plugin for the Intel DCX format.
41
+
42
+
43
+ class DcxImageFile(PcxImageFile):
44
+ format = "DCX"
45
+ format_description = "Intel DCX"
46
+ _close_exclusive_fp_after_loading = False
47
+
48
+ def _open(self) -> None:
49
+ # Header
50
+ assert self.fp is not None
51
+ s = self.fp.read(4)
52
+ if not _accept(s):
53
+ msg = "not a DCX file"
54
+ raise SyntaxError(msg)
55
+
56
+ # Component directory
57
+ self._offset = []
58
+ for i in range(1024):
59
+ offset = i32(self.fp.read(4))
60
+ if not offset:
61
+ break
62
+ self._offset.append(offset)
63
+
64
+ self._fp = self.fp
65
+ self.frame = -1
66
+ self.n_frames = len(self._offset)
67
+ self.is_animated = self.n_frames > 1
68
+ self.seek(0)
69
+
70
+ def seek(self, frame: int) -> None:
71
+ if not self._seek_check(frame):
72
+ return
73
+ if isinstance(self._fp, DeferredError):
74
+ raise self._fp.ex
75
+ self.frame = frame
76
+ self.fp = self._fp
77
+ self.fp.seek(self._offset[frame])
78
+ PcxImageFile._open(self)
79
+ Image._decompression_bomb_check(self.size)
80
+ Image.Image.seek(self, frame)
81
+
82
+ def tell(self) -> int:
83
+ return self.frame
84
+
85
+
86
+ Image.register_open(DcxImageFile.format, DcxImageFile, _accept)
87
+
88
+ Image.register_extension(DcxImageFile.format, ".dcx")
hug/lib/python3.12/site-packages/PIL/DdsImagePlugin.py ADDED
@@ -0,0 +1,637 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ A Pillow plugin for .dds files (S3TC-compressed aka DXTC)
3
+ Jerome Leclanche <jerome@leclan.ch>
4
+
5
+ Documentation:
6
+ https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt
7
+
8
+ The contents of this file are hereby released in the public domain (CC0)
9
+ Full text of the CC0 license:
10
+ https://creativecommons.org/publicdomain/zero/1.0/
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ __lazy_modules__ = {"struct"}
16
+
17
+ import struct
18
+ import sys
19
+ from enum import IntEnum, IntFlag
20
+
21
+ from . import Image, ImageFile, ImagePalette
22
+ from ._binary import i32le as i32
23
+ from ._binary import o8
24
+ from ._binary import o32le as o32
25
+
26
+ TYPE_CHECKING = False
27
+ if TYPE_CHECKING:
28
+ from typing import IO
29
+
30
+ # Magic ("DDS ")
31
+ DDS_MAGIC = 0x20534444
32
+
33
+
34
+ # DDS flags
35
+ class DDSD(IntFlag):
36
+ CAPS = 0x1
37
+ HEIGHT = 0x2
38
+ WIDTH = 0x4
39
+ PITCH = 0x8
40
+ PIXELFORMAT = 0x1000
41
+ MIPMAPCOUNT = 0x20000
42
+ LINEARSIZE = 0x80000
43
+ DEPTH = 0x800000
44
+
45
+
46
+ # DDS caps
47
+ class DDSCAPS(IntFlag):
48
+ COMPLEX = 0x8
49
+ TEXTURE = 0x1000
50
+ MIPMAP = 0x400000
51
+
52
+
53
+ class DDSCAPS2(IntFlag):
54
+ CUBEMAP = 0x200
55
+ CUBEMAP_POSITIVEX = 0x400
56
+ CUBEMAP_NEGATIVEX = 0x800
57
+ CUBEMAP_POSITIVEY = 0x1000
58
+ CUBEMAP_NEGATIVEY = 0x2000
59
+ CUBEMAP_POSITIVEZ = 0x4000
60
+ CUBEMAP_NEGATIVEZ = 0x8000
61
+ VOLUME = 0x200000
62
+
63
+
64
+ # Pixel Format
65
+ class DDPF(IntFlag):
66
+ ALPHAPIXELS = 0x1
67
+ ALPHA = 0x2
68
+ FOURCC = 0x4
69
+ PALETTEINDEXED8 = 0x20
70
+ RGB = 0x40
71
+ LUMINANCE = 0x20000
72
+
73
+
74
+ # dxgiformat.h
75
+ class DXGI_FORMAT(IntEnum):
76
+ UNKNOWN = 0
77
+ R32G32B32A32_TYPELESS = 1
78
+ R32G32B32A32_FLOAT = 2
79
+ R32G32B32A32_UINT = 3
80
+ R32G32B32A32_SINT = 4
81
+ R32G32B32_TYPELESS = 5
82
+ R32G32B32_FLOAT = 6
83
+ R32G32B32_UINT = 7
84
+ R32G32B32_SINT = 8
85
+ R16G16B16A16_TYPELESS = 9
86
+ R16G16B16A16_FLOAT = 10
87
+ R16G16B16A16_UNORM = 11
88
+ R16G16B16A16_UINT = 12
89
+ R16G16B16A16_SNORM = 13
90
+ R16G16B16A16_SINT = 14
91
+ R32G32_TYPELESS = 15
92
+ R32G32_FLOAT = 16
93
+ R32G32_UINT = 17
94
+ R32G32_SINT = 18
95
+ R32G8X24_TYPELESS = 19
96
+ D32_FLOAT_S8X24_UINT = 20
97
+ R32_FLOAT_X8X24_TYPELESS = 21
98
+ X32_TYPELESS_G8X24_UINT = 22
99
+ R10G10B10A2_TYPELESS = 23
100
+ R10G10B10A2_UNORM = 24
101
+ R10G10B10A2_UINT = 25
102
+ R11G11B10_FLOAT = 26
103
+ R8G8B8A8_TYPELESS = 27
104
+ R8G8B8A8_UNORM = 28
105
+ R8G8B8A8_UNORM_SRGB = 29
106
+ R8G8B8A8_UINT = 30
107
+ R8G8B8A8_SNORM = 31
108
+ R8G8B8A8_SINT = 32
109
+ R16G16_TYPELESS = 33
110
+ R16G16_FLOAT = 34
111
+ R16G16_UNORM = 35
112
+ R16G16_UINT = 36
113
+ R16G16_SNORM = 37
114
+ R16G16_SINT = 38
115
+ R32_TYPELESS = 39
116
+ D32_FLOAT = 40
117
+ R32_FLOAT = 41
118
+ R32_UINT = 42
119
+ R32_SINT = 43
120
+ R24G8_TYPELESS = 44
121
+ D24_UNORM_S8_UINT = 45
122
+ R24_UNORM_X8_TYPELESS = 46
123
+ X24_TYPELESS_G8_UINT = 47
124
+ R8G8_TYPELESS = 48
125
+ R8G8_UNORM = 49
126
+ R8G8_UINT = 50
127
+ R8G8_SNORM = 51
128
+ R8G8_SINT = 52
129
+ R16_TYPELESS = 53
130
+ R16_FLOAT = 54
131
+ D16_UNORM = 55
132
+ R16_UNORM = 56
133
+ R16_UINT = 57
134
+ R16_SNORM = 58
135
+ R16_SINT = 59
136
+ R8_TYPELESS = 60
137
+ R8_UNORM = 61
138
+ R8_UINT = 62
139
+ R8_SNORM = 63
140
+ R8_SINT = 64
141
+ A8_UNORM = 65
142
+ R1_UNORM = 66
143
+ R9G9B9E5_SHAREDEXP = 67
144
+ R8G8_B8G8_UNORM = 68
145
+ G8R8_G8B8_UNORM = 69
146
+ BC1_TYPELESS = 70
147
+ BC1_UNORM = 71
148
+ BC1_UNORM_SRGB = 72
149
+ BC2_TYPELESS = 73
150
+ BC2_UNORM = 74
151
+ BC2_UNORM_SRGB = 75
152
+ BC3_TYPELESS = 76
153
+ BC3_UNORM = 77
154
+ BC3_UNORM_SRGB = 78
155
+ BC4_TYPELESS = 79
156
+ BC4_UNORM = 80
157
+ BC4_SNORM = 81
158
+ BC5_TYPELESS = 82
159
+ BC5_UNORM = 83
160
+ BC5_SNORM = 84
161
+ B5G6R5_UNORM = 85
162
+ B5G5R5A1_UNORM = 86
163
+ B8G8R8A8_UNORM = 87
164
+ B8G8R8X8_UNORM = 88
165
+ R10G10B10_XR_BIAS_A2_UNORM = 89
166
+ B8G8R8A8_TYPELESS = 90
167
+ B8G8R8A8_UNORM_SRGB = 91
168
+ B8G8R8X8_TYPELESS = 92
169
+ B8G8R8X8_UNORM_SRGB = 93
170
+ BC6H_TYPELESS = 94
171
+ BC6H_UF16 = 95
172
+ BC6H_SF16 = 96
173
+ BC7_TYPELESS = 97
174
+ BC7_UNORM = 98
175
+ BC7_UNORM_SRGB = 99
176
+ AYUV = 100
177
+ Y410 = 101
178
+ Y416 = 102
179
+ NV12 = 103
180
+ P010 = 104
181
+ P016 = 105
182
+ OPAQUE_420 = 106
183
+ YUY2 = 107
184
+ Y210 = 108
185
+ Y216 = 109
186
+ NV11 = 110
187
+ AI44 = 111
188
+ IA44 = 112
189
+ P8 = 113
190
+ A8P8 = 114
191
+ B4G4R4A4_UNORM = 115
192
+ P208 = 130
193
+ V208 = 131
194
+ V408 = 132
195
+ SAMPLER_FEEDBACK_MIN_MIP_OPAQUE = 189
196
+ SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE = 190
197
+
198
+
199
+ class D3DFMT(IntEnum):
200
+ UNKNOWN = 0
201
+ R8G8B8 = 20
202
+ A8R8G8B8 = 21
203
+ X8R8G8B8 = 22
204
+ R5G6B5 = 23
205
+ X1R5G5B5 = 24
206
+ A1R5G5B5 = 25
207
+ A4R4G4B4 = 26
208
+ R3G3B2 = 27
209
+ A8 = 28
210
+ A8R3G3B2 = 29
211
+ X4R4G4B4 = 30
212
+ A2B10G10R10 = 31
213
+ A8B8G8R8 = 32
214
+ X8B8G8R8 = 33
215
+ G16R16 = 34
216
+ A2R10G10B10 = 35
217
+ A16B16G16R16 = 36
218
+ A8P8 = 40
219
+ P8 = 41
220
+ L8 = 50
221
+ A8L8 = 51
222
+ A4L4 = 52
223
+ V8U8 = 60
224
+ L6V5U5 = 61
225
+ X8L8V8U8 = 62
226
+ Q8W8V8U8 = 63
227
+ V16U16 = 64
228
+ A2W10V10U10 = 67
229
+ D16_LOCKABLE = 70
230
+ D32 = 71
231
+ D15S1 = 73
232
+ D24S8 = 75
233
+ D24X8 = 77
234
+ D24X4S4 = 79
235
+ D16 = 80
236
+ D32F_LOCKABLE = 82
237
+ D24FS8 = 83
238
+ D32_LOCKABLE = 84
239
+ S8_LOCKABLE = 85
240
+ L16 = 81
241
+ VERTEXDATA = 100
242
+ INDEX16 = 101
243
+ INDEX32 = 102
244
+ Q16W16V16U16 = 110
245
+ R16F = 111
246
+ G16R16F = 112
247
+ A16B16G16R16F = 113
248
+ R32F = 114
249
+ G32R32F = 115
250
+ A32B32G32R32F = 116
251
+ CxV8U8 = 117
252
+ A1 = 118
253
+ A2B10G10R10_XR_BIAS = 119
254
+ BINARYBUFFER = 199
255
+
256
+ UYVY = i32(b"UYVY")
257
+ R8G8_B8G8 = i32(b"RGBG")
258
+ YUY2 = i32(b"YUY2")
259
+ G8R8_G8B8 = i32(b"GRGB")
260
+ DXT1 = i32(b"DXT1")
261
+ DXT2 = i32(b"DXT2")
262
+ DXT3 = i32(b"DXT3")
263
+ DXT4 = i32(b"DXT4")
264
+ DXT5 = i32(b"DXT5")
265
+ DX10 = i32(b"DX10")
266
+ BC4S = i32(b"BC4S")
267
+ BC4U = i32(b"BC4U")
268
+ BC5S = i32(b"BC5S")
269
+ BC5U = i32(b"BC5U")
270
+ ATI1 = i32(b"ATI1")
271
+ ATI2 = i32(b"ATI2")
272
+ MULTI2_ARGB8 = i32(b"MET1")
273
+
274
+
275
+ # Backward compatibility layer
276
+ module = sys.modules[__name__]
277
+ for item in DDSD:
278
+ assert item.name is not None
279
+ setattr(module, f"DDSD_{item.name}", item.value)
280
+ for item1 in DDSCAPS:
281
+ assert item1.name is not None
282
+ setattr(module, f"DDSCAPS_{item1.name}", item1.value)
283
+ for item2 in DDSCAPS2:
284
+ assert item2.name is not None
285
+ setattr(module, f"DDSCAPS2_{item2.name}", item2.value)
286
+ for item3 in DDPF:
287
+ assert item3.name is not None
288
+ setattr(module, f"DDPF_{item3.name}", item3.value)
289
+
290
+ DDS_FOURCC = DDPF.FOURCC
291
+ DDS_RGB = DDPF.RGB
292
+ DDS_RGBA = DDPF.RGB | DDPF.ALPHAPIXELS
293
+ DDS_LUMINANCE = DDPF.LUMINANCE
294
+ DDS_LUMINANCEA = DDPF.LUMINANCE | DDPF.ALPHAPIXELS
295
+ DDS_ALPHA = DDPF.ALPHA
296
+ DDS_PAL8 = DDPF.PALETTEINDEXED8
297
+
298
+ DDS_HEADER_FLAGS_TEXTURE = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT
299
+ DDS_HEADER_FLAGS_MIPMAP = DDSD.MIPMAPCOUNT
300
+ DDS_HEADER_FLAGS_VOLUME = DDSD.DEPTH
301
+ DDS_HEADER_FLAGS_PITCH = DDSD.PITCH
302
+ DDS_HEADER_FLAGS_LINEARSIZE = DDSD.LINEARSIZE
303
+
304
+ DDS_HEIGHT = DDSD.HEIGHT
305
+ DDS_WIDTH = DDSD.WIDTH
306
+
307
+ DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS.TEXTURE
308
+ DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS.COMPLEX | DDSCAPS.MIPMAP
309
+ DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS.COMPLEX
310
+
311
+ DDS_CUBEMAP_POSITIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEX
312
+ DDS_CUBEMAP_NEGATIVEX = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEX
313
+ DDS_CUBEMAP_POSITIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEY
314
+ DDS_CUBEMAP_NEGATIVEY = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEY
315
+ DDS_CUBEMAP_POSITIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_POSITIVEZ
316
+ DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2.CUBEMAP | DDSCAPS2.CUBEMAP_NEGATIVEZ
317
+
318
+ DXT1_FOURCC = D3DFMT.DXT1
319
+ DXT3_FOURCC = D3DFMT.DXT3
320
+ DXT5_FOURCC = D3DFMT.DXT5
321
+
322
+ DXGI_FORMAT_R8G8B8A8_TYPELESS = DXGI_FORMAT.R8G8B8A8_TYPELESS
323
+ DXGI_FORMAT_R8G8B8A8_UNORM = DXGI_FORMAT.R8G8B8A8_UNORM
324
+ DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = DXGI_FORMAT.R8G8B8A8_UNORM_SRGB
325
+ DXGI_FORMAT_BC5_TYPELESS = DXGI_FORMAT.BC5_TYPELESS
326
+ DXGI_FORMAT_BC5_UNORM = DXGI_FORMAT.BC5_UNORM
327
+ DXGI_FORMAT_BC5_SNORM = DXGI_FORMAT.BC5_SNORM
328
+ DXGI_FORMAT_BC6H_UF16 = DXGI_FORMAT.BC6H_UF16
329
+ DXGI_FORMAT_BC6H_SF16 = DXGI_FORMAT.BC6H_SF16
330
+ DXGI_FORMAT_BC7_TYPELESS = DXGI_FORMAT.BC7_TYPELESS
331
+ DXGI_FORMAT_BC7_UNORM = DXGI_FORMAT.BC7_UNORM
332
+ DXGI_FORMAT_BC7_UNORM_SRGB = DXGI_FORMAT.BC7_UNORM_SRGB
333
+
334
+
335
+ class DdsImageFile(ImageFile.ImageFile):
336
+ format = "DDS"
337
+ format_description = "DirectDraw Surface"
338
+
339
+ def _open(self) -> None:
340
+ assert self.fp is not None
341
+ if not _accept(self.fp.read(4)):
342
+ msg = "not a DDS file"
343
+ raise SyntaxError(msg)
344
+ (header_size,) = struct.unpack("<I", self.fp.read(4))
345
+ if header_size != 124:
346
+ msg = f"Unsupported header size {repr(header_size)}"
347
+ raise OSError(msg)
348
+ header = self.fp.read(header_size - 4)
349
+ if len(header) != 120:
350
+ msg = f"Incomplete header: {len(header)} bytes"
351
+ raise OSError(msg)
352
+
353
+ flags, height, width = struct.unpack("<3I", header[:12])
354
+ self._size = (width, height)
355
+ extents = (0, 0, *self.size)
356
+
357
+ pitch, depth, mipmaps = struct.unpack("<3I", header[12:24])
358
+ struct.unpack("<11I", header[24:68]) # reserved
359
+
360
+ # pixel format
361
+ pfsize, pfflags, fourcc, bitcount = struct.unpack("<4I", header[68:84])
362
+ n = 0
363
+ rawmode = None
364
+ if pfflags & DDPF.RGB:
365
+ # Texture contains uncompressed RGB data
366
+ if pfflags & DDPF.ALPHAPIXELS:
367
+ self._mode = "RGBA"
368
+ mask_count = 4
369
+ else:
370
+ self._mode = "RGB"
371
+ mask_count = 3
372
+
373
+ masks = struct.unpack(f"<{mask_count}I", header[84 : 84 + mask_count * 4])
374
+ self.tile = [ImageFile._Tile("dds_rgb", extents, 0, (bitcount, masks))]
375
+ return
376
+ elif pfflags & DDPF.LUMINANCE:
377
+ if bitcount == 8:
378
+ self._mode = "L"
379
+ elif bitcount == 16 and pfflags & DDPF.ALPHAPIXELS:
380
+ self._mode = "LA"
381
+ else:
382
+ msg = f"Unsupported bitcount {bitcount} for {pfflags}"
383
+ raise OSError(msg)
384
+ elif pfflags & DDPF.PALETTEINDEXED8:
385
+ self._mode = "P"
386
+ self.palette = ImagePalette.ImagePalette("RGBA", self.fp.read(1024))
387
+ self.palette.dirty = 1
388
+ elif pfflags & DDPF.FOURCC:
389
+ offset = header_size + 4
390
+ if fourcc == D3DFMT.DXT1:
391
+ self._mode = "RGBA"
392
+ self.pixel_format = "DXT1"
393
+ n = 1
394
+ elif fourcc == D3DFMT.DXT3:
395
+ self._mode = "RGBA"
396
+ self.pixel_format = "DXT3"
397
+ n = 2
398
+ elif fourcc == D3DFMT.DXT5:
399
+ self._mode = "RGBA"
400
+ self.pixel_format = "DXT5"
401
+ n = 3
402
+ elif fourcc in (D3DFMT.BC4U, D3DFMT.ATI1):
403
+ self._mode = "L"
404
+ self.pixel_format = "BC4"
405
+ n = 4
406
+ elif fourcc == D3DFMT.BC5S:
407
+ self._mode = "RGB"
408
+ self.pixel_format = "BC5S"
409
+ n = 5
410
+ elif fourcc in (D3DFMT.BC5U, D3DFMT.ATI2):
411
+ self._mode = "RGB"
412
+ self.pixel_format = "BC5"
413
+ n = 5
414
+ elif fourcc == D3DFMT.DX10:
415
+ offset += 20
416
+ # ignoring flags which pertain to volume textures and cubemaps
417
+ (dxgi_format,) = struct.unpack("<I", self.fp.read(4))
418
+ self.fp.read(16)
419
+ if dxgi_format in (
420
+ DXGI_FORMAT.BC1_UNORM,
421
+ DXGI_FORMAT.BC1_TYPELESS,
422
+ ):
423
+ self._mode = "RGBA"
424
+ self.pixel_format = "BC1"
425
+ n = 1
426
+ elif dxgi_format in (DXGI_FORMAT.BC2_TYPELESS, DXGI_FORMAT.BC2_UNORM):
427
+ self._mode = "RGBA"
428
+ self.pixel_format = "BC2"
429
+ n = 2
430
+ elif dxgi_format in (DXGI_FORMAT.BC3_TYPELESS, DXGI_FORMAT.BC3_UNORM):
431
+ self._mode = "RGBA"
432
+ self.pixel_format = "BC3"
433
+ n = 3
434
+ elif dxgi_format in (DXGI_FORMAT.BC4_TYPELESS, DXGI_FORMAT.BC4_UNORM):
435
+ self._mode = "L"
436
+ self.pixel_format = "BC4"
437
+ n = 4
438
+ elif dxgi_format in (DXGI_FORMAT.BC5_TYPELESS, DXGI_FORMAT.BC5_UNORM):
439
+ self._mode = "RGB"
440
+ self.pixel_format = "BC5"
441
+ n = 5
442
+ elif dxgi_format == DXGI_FORMAT.BC5_SNORM:
443
+ self._mode = "RGB"
444
+ self.pixel_format = "BC5S"
445
+ n = 5
446
+ elif dxgi_format == DXGI_FORMAT.BC6H_UF16:
447
+ self._mode = "RGB"
448
+ self.pixel_format = "BC6H"
449
+ n = 6
450
+ elif dxgi_format == DXGI_FORMAT.BC6H_SF16:
451
+ self._mode = "RGB"
452
+ self.pixel_format = "BC6HS"
453
+ n = 6
454
+ elif dxgi_format in (
455
+ DXGI_FORMAT.BC7_TYPELESS,
456
+ DXGI_FORMAT.BC7_UNORM,
457
+ DXGI_FORMAT.BC7_UNORM_SRGB,
458
+ ):
459
+ self._mode = "RGBA"
460
+ self.pixel_format = "BC7"
461
+ n = 7
462
+ if dxgi_format == DXGI_FORMAT.BC7_UNORM_SRGB:
463
+ self.info["gamma"] = 1 / 2.2
464
+ elif dxgi_format in (
465
+ DXGI_FORMAT.R8G8B8A8_TYPELESS,
466
+ DXGI_FORMAT.R8G8B8A8_UNORM,
467
+ DXGI_FORMAT.R8G8B8A8_UNORM_SRGB,
468
+ ):
469
+ self._mode = "RGBA"
470
+ if dxgi_format == DXGI_FORMAT.R8G8B8A8_UNORM_SRGB:
471
+ self.info["gamma"] = 1 / 2.2
472
+ else:
473
+ msg = f"Unimplemented DXGI format {dxgi_format}"
474
+ raise NotImplementedError(msg)
475
+ else:
476
+ msg = f"Unimplemented pixel format {repr(fourcc)}"
477
+ raise NotImplementedError(msg)
478
+ else:
479
+ msg = f"Unknown pixel format flags {pfflags}"
480
+ raise NotImplementedError(msg)
481
+
482
+ if n:
483
+ self.tile = [
484
+ ImageFile._Tile("bcn", extents, offset, (n, self.pixel_format))
485
+ ]
486
+ else:
487
+ self.tile = [ImageFile._Tile("raw", extents, 0, rawmode or self.mode)]
488
+
489
+ def load_seek(self, pos: int) -> None:
490
+ pass
491
+
492
+
493
+ class DdsRgbDecoder(ImageFile.PyDecoder):
494
+ _pulls_fd = True
495
+
496
+ def decode(self, buffer: Image.DecoderInput) -> tuple[int, int]:
497
+ bitcount, masks = self.args
498
+
499
+ data = bytearray()
500
+ bytecount = bitcount // 8
501
+ if not bytecount:
502
+ self.set_as_raw(data)
503
+ return -1, 0
504
+
505
+ # Some masks will be padded with zeros, e.g. R 0b11 G 0b1100
506
+ # Calculate how many zeros each mask is padded with
507
+ mask_offsets = []
508
+ # And the maximum value of each channel without the padding
509
+ mask_totals = []
510
+ for mask in masks:
511
+ offset = 0
512
+ if mask != 0:
513
+ while mask >> (offset + 1) << (offset + 1) == mask:
514
+ offset += 1
515
+ mask_offsets.append(offset)
516
+ mask_totals.append(mask >> offset)
517
+
518
+ assert self.fd is not None
519
+ dest_length = self.state.xsize * self.state.ysize * len(masks)
520
+ while len(data) < dest_length:
521
+ bytes_read = self.fd.read(bytecount)
522
+ if len(bytes_read) < bytecount:
523
+ break
524
+ value = int.from_bytes(bytes_read, "little")
525
+ for i, mask in enumerate(masks):
526
+ masked_value = value & mask
527
+ # Remove the zero padding, and scale it to 8 bits
528
+ data += o8(
529
+ int(((masked_value >> mask_offsets[i]) / mask_totals[i]) * 255)
530
+ if mask_totals[i]
531
+ else 0
532
+ )
533
+ self.set_as_raw(data)
534
+ return -1, 0
535
+
536
+
537
+ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None:
538
+ if im.mode not in ("RGB", "RGBA", "L", "LA"):
539
+ msg = f"cannot write mode {im.mode} as DDS"
540
+ raise OSError(msg)
541
+
542
+ flags = DDSD.CAPS | DDSD.HEIGHT | DDSD.WIDTH | DDSD.PIXELFORMAT
543
+ bitcount = len(im.getbands()) * 8
544
+ pixel_format = im.encoderinfo.get("pixel_format")
545
+ args: tuple[int] | str
546
+ if pixel_format:
547
+ codec_name = "bcn"
548
+ flags |= DDSD.LINEARSIZE
549
+ pitch = (im.width + 3) * 4
550
+ rgba_mask = [0, 0, 0, 0]
551
+ pixel_flags = DDPF.FOURCC
552
+ if pixel_format == "DXT1":
553
+ fourcc = D3DFMT.DXT1
554
+ args = (1,)
555
+ elif pixel_format == "DXT3":
556
+ fourcc = D3DFMT.DXT3
557
+ args = (2,)
558
+ elif pixel_format == "DXT5":
559
+ fourcc = D3DFMT.DXT5
560
+ args = (3,)
561
+ else:
562
+ fourcc = D3DFMT.DX10
563
+ if pixel_format == "BC2":
564
+ args = (2,)
565
+ dxgi_format = DXGI_FORMAT.BC2_TYPELESS
566
+ elif pixel_format == "BC3":
567
+ args = (3,)
568
+ dxgi_format = DXGI_FORMAT.BC3_TYPELESS
569
+ elif pixel_format == "BC5":
570
+ args = (5,)
571
+ dxgi_format = DXGI_FORMAT.BC5_TYPELESS
572
+ if im.mode != "RGB":
573
+ msg = "only RGB mode can be written as BC5"
574
+ raise OSError(msg)
575
+ else:
576
+ msg = f"cannot write pixel format {pixel_format}"
577
+ raise OSError(msg)
578
+ else:
579
+ codec_name = "raw"
580
+ flags |= DDSD.PITCH
581
+ pitch = (im.width * bitcount + 7) // 8
582
+
583
+ alpha = im.mode[-1] == "A"
584
+ if im.mode[0] == "L":
585
+ pixel_flags = DDPF.LUMINANCE
586
+ args = im.mode
587
+ if alpha:
588
+ rgba_mask = [0x000000FF, 0x000000FF, 0x000000FF]
589
+ else:
590
+ rgba_mask = [0xFF000000, 0xFF000000, 0xFF000000]
591
+ else:
592
+ pixel_flags = DDPF.RGB
593
+ args = im.mode[::-1]
594
+ rgba_mask = [0x00FF0000, 0x0000FF00, 0x000000FF]
595
+
596
+ if alpha:
597
+ r, g, b, a = im.split()
598
+ im = Image.merge("RGBA", (a, r, g, b))
599
+ if alpha:
600
+ pixel_flags |= DDPF.ALPHAPIXELS
601
+ rgba_mask.append(0xFF000000 if alpha else 0)
602
+
603
+ fourcc = D3DFMT.UNKNOWN
604
+ fp.write(
605
+ o32(DDS_MAGIC)
606
+ + struct.pack(
607
+ "<7I",
608
+ 124, # header size
609
+ flags, # flags
610
+ im.height,
611
+ im.width,
612
+ pitch,
613
+ 0, # depth
614
+ 0, # mipmaps
615
+ )
616
+ + struct.pack("11I", *((0,) * 11)) # reserved
617
+ # pfsize, pfflags, fourcc, bitcount
618
+ + struct.pack("<4I", 32, pixel_flags, fourcc, bitcount)
619
+ + struct.pack("<4I", *rgba_mask) # dwRGBABitMask
620
+ + struct.pack("<5I", DDSCAPS.TEXTURE, 0, 0, 0, 0)
621
+ )
622
+ if fourcc == D3DFMT.DX10:
623
+ fp.write(
624
+ # dxgi_format, 2D resource, misc, array size, straight alpha
625
+ struct.pack("<5I", dxgi_format, 3, 0, 0, 1)
626
+ )
627
+ ImageFile._save(im, fp, [ImageFile._Tile(codec_name, (0, 0, *im.size), 0, args)])
628
+
629
+
630
+ def _accept(prefix: bytes) -> bool:
631
+ return prefix.startswith(b"DDS ")
632
+
633
+
634
+ Image.register_open(DdsImageFile.format, DdsImageFile, _accept)
635
+ Image.register_decoder("dds_rgb", DdsRgbDecoder)
636
+ Image.register_save(DdsImageFile.format, _save)
637
+ Image.register_extension(DdsImageFile.format, ".dds")
hug/lib/python3.12/site-packages/PIL/EpsImagePlugin.py ADDED
@@ -0,0 +1,494 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # EPS file handling
6
+ #
7
+ # History:
8
+ # 1995-09-01 fl Created (0.1)
9
+ # 1996-05-18 fl Don't choke on "atend" fields, Ghostscript interface (0.2)
10
+ # 1996-08-22 fl Don't choke on floating point BoundingBox values
11
+ # 1996-08-23 fl Handle files from Macintosh (0.3)
12
+ # 2001-02-17 fl Use 're' instead of 'regex' (Python 2.1) (0.4)
13
+ # 2003-09-07 fl Check gs.close status (from Federico Di Gregorio) (0.5)
14
+ # 2014-05-07 e Handling of EPS with binary preview and fixed resolution
15
+ # resizing
16
+ #
17
+ # Copyright (c) 1997-2003 by Secret Labs AB.
18
+ # Copyright (c) 1995-2003 by Fredrik Lundh
19
+ #
20
+ # See the README file for information on usage and redistribution.
21
+ #
22
+ from __future__ import annotations
23
+
24
+ __lazy_modules__ = {"PIL._binary", "io", "subprocess"}
25
+
26
+ import io
27
+ import os
28
+ import re
29
+ import subprocess
30
+ import sys
31
+
32
+ from . import Image, ImageFile
33
+ from ._binary import i32le as i32
34
+
35
+ TYPE_CHECKING = False
36
+ if TYPE_CHECKING:
37
+ from typing import IO
38
+
39
+ # --------------------------------------------------------------------
40
+
41
+
42
+ split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
43
+ field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
44
+
45
+ gs_binary: str | bool | None = None
46
+ gs_windows_binary = None
47
+
48
+
49
+ def has_ghostscript() -> bool:
50
+ global gs_binary, gs_windows_binary
51
+ if gs_binary is None:
52
+ if sys.platform.startswith("win"):
53
+ if gs_windows_binary is None:
54
+ import shutil
55
+
56
+ for binary in ("gswin32c", "gswin64c", "gs"):
57
+ if shutil.which(binary) is not None:
58
+ gs_windows_binary = binary
59
+ break
60
+ else:
61
+ gs_windows_binary = False
62
+ gs_binary = gs_windows_binary
63
+ else:
64
+ try:
65
+ subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL)
66
+ gs_binary = "gs"
67
+ except OSError:
68
+ gs_binary = False
69
+ return gs_binary is not False
70
+
71
+
72
+ def Ghostscript(
73
+ tile: list[ImageFile._Tile],
74
+ size: tuple[int, int],
75
+ fp: IO[bytes],
76
+ scale: int = 1,
77
+ transparency: bool = False,
78
+ ) -> Image.core.ImagingCore:
79
+ """Render an image using Ghostscript"""
80
+ global gs_binary
81
+ if not has_ghostscript():
82
+ msg = "Unable to locate Ghostscript on paths"
83
+ raise OSError(msg)
84
+ assert isinstance(gs_binary, str)
85
+
86
+ # Unpack decoder tile
87
+ args = tile[0].args
88
+ assert isinstance(args, tuple)
89
+ length, bbox = args
90
+
91
+ # Hack to support hi-res rendering
92
+ scale = int(scale) or 1
93
+ width = size[0] * scale
94
+ height = size[1] * scale
95
+ Image._decompression_bomb_check((width, height))
96
+ # resolution is dependent on bbox and size
97
+ res_x = 72.0 * width / (bbox[2] - bbox[0])
98
+ res_y = 72.0 * height / (bbox[3] - bbox[1])
99
+
100
+ import tempfile
101
+
102
+ out_fd, outfile = tempfile.mkstemp()
103
+ os.close(out_fd)
104
+
105
+ infile_temp = None
106
+ if hasattr(fp, "name") and os.path.exists(fp.name):
107
+ # Make the path absolute so that a filename beginning with "-" or "@"
108
+ # can never be interpreted by Ghostscript's argument parser
109
+ # as an option or an @-file token
110
+ infile = os.path.abspath(fp.name)
111
+ else:
112
+ in_fd, infile_temp = tempfile.mkstemp()
113
+ os.close(in_fd)
114
+ infile = infile_temp
115
+
116
+ # Ignore length and offset!
117
+ # Ghostscript can read it
118
+ # Copy whole file to read in Ghostscript
119
+ with open(infile_temp, "wb") as f:
120
+ # fetch length of fp
121
+ fp.seek(0, io.SEEK_END)
122
+ fsize = fp.tell()
123
+ # ensure start position
124
+ # go back
125
+ fp.seek(0)
126
+ lengthfile = fsize
127
+ while lengthfile > 0:
128
+ s = fp.read(min(lengthfile, 100 * 1024))
129
+ if not s:
130
+ break
131
+ lengthfile -= len(s)
132
+ f.write(s)
133
+
134
+ if transparency:
135
+ # "RGBA"
136
+ device = "pngalpha"
137
+ else:
138
+ # "pnmraw" automatically chooses between
139
+ # PBM ("1"), PGM ("L"), and PPM ("RGB").
140
+ device = "pnmraw"
141
+
142
+ # Build Ghostscript command
143
+ command = [
144
+ gs_binary,
145
+ "-q", # quiet mode
146
+ f"-g{width:d}x{height:d}", # set output geometry (pixels)
147
+ f"-r{res_x:f}x{res_y:f}", # set input DPI (dots per inch)
148
+ "-dBATCH", # exit after processing
149
+ "-dNOPAUSE", # don't pause between pages
150
+ "-dSAFER", # safe mode
151
+ f"-sDEVICE={device}",
152
+ f"-sOutputFile={outfile}", # output file
153
+ # adjust for image origin
154
+ "-c",
155
+ f"{-bbox[0]} {-bbox[1]} translate",
156
+ "-f",
157
+ infile, # input file
158
+ # showpage (see https://bugs.ghostscript.com/show_bug.cgi?id=698272)
159
+ "-c",
160
+ "showpage",
161
+ ]
162
+
163
+ # push data through Ghostscript
164
+ try:
165
+ startupinfo = None
166
+ if sys.platform.startswith("win"):
167
+ startupinfo = subprocess.STARTUPINFO()
168
+ startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
169
+ subprocess.check_call(command, startupinfo=startupinfo)
170
+ with Image.open(outfile) as out_im:
171
+ out_im.load()
172
+ return out_im.im.copy()
173
+ finally:
174
+ try:
175
+ os.unlink(outfile)
176
+ if infile_temp:
177
+ os.unlink(infile_temp)
178
+ except OSError:
179
+ pass
180
+
181
+
182
+ def _accept(prefix: bytes) -> bool:
183
+ return prefix.startswith(b"%!PS") or (
184
+ len(prefix) >= 4 and i32(prefix) == 0xC6D3D0C5
185
+ )
186
+
187
+
188
+ ##
189
+ # Image plugin for Encapsulated PostScript. This plugin supports only
190
+ # a few variants of this format.
191
+
192
+
193
+ class EpsImageFile(ImageFile.ImageFile):
194
+ """EPS File Parser for the Python Imaging Library"""
195
+
196
+ format = "EPS"
197
+ format_description = "Encapsulated Postscript"
198
+
199
+ mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
200
+
201
+ def _open(self) -> None:
202
+ assert self.fp is not None
203
+ length, offset = self._find_offset(self.fp)
204
+
205
+ # go to offset - start of "%!PS"
206
+ self.fp.seek(offset)
207
+
208
+ self._mode = "RGB"
209
+
210
+ # When reading header comments, the first comment is used.
211
+ # When reading trailer comments, the last comment is used.
212
+ bounding_box: list[int] | None = None
213
+ imagedata_size: tuple[int, int] | None = None
214
+
215
+ byte_arr = bytearray(255)
216
+ bytes_mv = memoryview(byte_arr)
217
+ bytes_read = 0
218
+ reading_header_comments = True
219
+ reading_trailer_comments = False
220
+ trailer_reached = False
221
+
222
+ def check_required_header_comments() -> None:
223
+ """
224
+ The EPS specification requires that some headers exist.
225
+ This should be checked when the header comments formally end,
226
+ when image data starts, or when the file ends, whichever comes first.
227
+ """
228
+ if "PS-Adobe" not in self.info:
229
+ msg = 'EPS header missing "%!PS-Adobe" comment'
230
+ raise SyntaxError(msg)
231
+ if "BoundingBox" not in self.info:
232
+ msg = 'EPS header missing "%%BoundingBox" comment'
233
+ raise SyntaxError(msg)
234
+
235
+ def read_comment(s: str) -> bool:
236
+ nonlocal bounding_box, reading_trailer_comments
237
+ try:
238
+ m = split.match(s)
239
+ except re.error as e:
240
+ msg = "not an EPS file"
241
+ raise SyntaxError(msg) from e
242
+
243
+ if not m:
244
+ return False
245
+
246
+ k, v = m.group(1, 2)
247
+ self.info[k] = v
248
+ if k == "BoundingBox":
249
+ if v == "(atend)":
250
+ reading_trailer_comments = True
251
+ elif not bounding_box or (trailer_reached and reading_trailer_comments):
252
+ try:
253
+ # Note: The DSC spec says that BoundingBox
254
+ # fields should be integers, but some drivers
255
+ # put floating point values there anyway.
256
+ bounding_box = [int(float(i)) for i in v.split()]
257
+ except Exception:
258
+ pass
259
+ return True
260
+
261
+ while True:
262
+ byte = self.fp.read(1)
263
+ if byte == b"":
264
+ # if we didn't read a byte we must be at the end of the file
265
+ if bytes_read == 0:
266
+ if reading_header_comments:
267
+ check_required_header_comments()
268
+ break
269
+ elif byte in b"\r\n":
270
+ # if we read a line ending character, ignore it and parse what
271
+ # we have already read. if we haven't read any other characters,
272
+ # continue reading
273
+ if bytes_read == 0:
274
+ continue
275
+ else:
276
+ # ASCII/hexadecimal lines in an EPS file must not exceed
277
+ # 255 characters, not including line ending characters
278
+ if bytes_read >= 255:
279
+ # only enforce this for lines starting with a "%",
280
+ # otherwise assume it's binary data
281
+ if byte_arr[0] == ord("%"):
282
+ msg = "not an EPS file"
283
+ raise SyntaxError(msg)
284
+ else:
285
+ if reading_header_comments:
286
+ check_required_header_comments()
287
+ reading_header_comments = False
288
+ # reset bytes_read so we can keep reading
289
+ # data until the end of the line
290
+ bytes_read = 0
291
+ byte_arr[bytes_read] = byte[0]
292
+ bytes_read += 1
293
+ continue
294
+
295
+ if reading_header_comments:
296
+ # Load EPS header
297
+
298
+ # if this line doesn't start with a "%",
299
+ # or does start with "%%EndComments",
300
+ # then we've reached the end of the header/comments
301
+ if byte_arr[0] != ord("%") or bytes_mv[:13] == b"%%EndComments":
302
+ check_required_header_comments()
303
+ reading_header_comments = False
304
+ continue
305
+
306
+ s = str(bytes_mv[:bytes_read], "latin-1")
307
+ if not read_comment(s):
308
+ m = field.match(s)
309
+ if m:
310
+ k = m.group(1)
311
+ if k.startswith("PS-Adobe"):
312
+ self.info["PS-Adobe"] = k[9:]
313
+ else:
314
+ self.info[k] = ""
315
+ elif s[0] == "%":
316
+ # handle non-DSC PostScript comments that some
317
+ # tools mistakenly put in the Comments section
318
+ pass
319
+ else:
320
+ msg = "bad EPS header"
321
+ raise OSError(msg)
322
+ elif bytes_mv[:11] == b"%ImageData:":
323
+ # Check for an "ImageData" descriptor
324
+ # https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577413_pgfId-1035096
325
+
326
+ # If we've already read an "ImageData" descriptor,
327
+ # don't read another one.
328
+ if imagedata_size:
329
+ bytes_read = 0
330
+ continue
331
+
332
+ # Values:
333
+ # columns
334
+ # rows
335
+ # bit depth (1 or 8)
336
+ # mode (1: L, 2: LAB, 3: RGB, 4: CMYK)
337
+ # number of padding channels
338
+ # block size (number of bytes per row per channel)
339
+ # binary/ascii (1: binary, 2: ascii)
340
+ # data start identifier (the image data follows after a single line
341
+ # consisting only of this quoted value)
342
+ image_data_values = byte_arr[11:bytes_read].split(None, 7)
343
+ columns, rows, bit_depth, mode_id = (
344
+ int(value) for value in image_data_values[:4]
345
+ )
346
+
347
+ if bit_depth == 1:
348
+ self._mode = "1"
349
+ elif bit_depth == 8:
350
+ try:
351
+ self._mode = self.mode_map[mode_id]
352
+ except ValueError:
353
+ break
354
+ else:
355
+ break
356
+
357
+ # Parse the columns and rows after checking the bit depth and mode
358
+ # in case the bit depth and/or mode are invalid.
359
+ imagedata_size = columns, rows
360
+ elif bytes_mv[:5] == b"%%EOF":
361
+ break
362
+ elif trailer_reached and reading_trailer_comments:
363
+ # Load EPS trailer
364
+ s = str(bytes_mv[:bytes_read], "latin-1")
365
+ read_comment(s)
366
+ elif bytes_mv[:9] == b"%%Trailer":
367
+ trailer_reached = True
368
+ elif bytes_mv[:14] == b"%%BeginBinary:":
369
+ bytecount = int(byte_arr[14:bytes_read])
370
+ if bytecount < 0:
371
+ msg = "BeginBinary bytecount cannot be negative"
372
+ raise ValueError(msg)
373
+ self.fp.seek(bytecount, os.SEEK_CUR)
374
+ bytes_read = 0
375
+
376
+ # A "BoundingBox" is always required,
377
+ # even if an "ImageData" descriptor size exists.
378
+ if not bounding_box:
379
+ msg = "cannot determine EPS bounding box"
380
+ raise OSError(msg)
381
+
382
+ # An "ImageData" size takes precedence over the "BoundingBox".
383
+ self._size = imagedata_size or (
384
+ bounding_box[2] - bounding_box[0],
385
+ bounding_box[3] - bounding_box[1],
386
+ )
387
+
388
+ self.tile = [
389
+ ImageFile._Tile("eps", (0, 0, *self.size), offset, (length, bounding_box))
390
+ ]
391
+
392
+ def _find_offset(self, fp: IO[bytes]) -> tuple[int, int]:
393
+ s = fp.read(4)
394
+
395
+ if s == b"%!PS":
396
+ # for HEAD without binary preview
397
+ fp.seek(0, io.SEEK_END)
398
+ length = fp.tell()
399
+ offset = 0
400
+ elif i32(s) == 0xC6D3D0C5:
401
+ # FIX for: Some EPS file not handled correctly / issue #302
402
+ # EPS can contain binary data
403
+ # or start directly with latin coding
404
+ # more info see:
405
+ # https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
406
+ s = fp.read(8)
407
+ offset = i32(s)
408
+ length = i32(s, 4)
409
+ else:
410
+ msg = "not an EPS file"
411
+ raise SyntaxError(msg)
412
+
413
+ return length, offset
414
+
415
+ def load(
416
+ self, scale: int = 1, transparency: bool = False
417
+ ) -> Image.core.PixelAccess | None:
418
+ # Load EPS via Ghostscript
419
+ if self.tile:
420
+ assert self.fp is not None
421
+ self.im = Ghostscript(self.tile, self.size, self.fp, scale, transparency)
422
+ self._mode = self.im.mode
423
+ self._size = self.im.size
424
+ self.tile = []
425
+ return Image.Image.load(self)
426
+
427
+ def load_seek(self, pos: int) -> None:
428
+ # we can't incrementally load, so force ImageFile.parser to
429
+ # use our custom load method by defining this method.
430
+ pass
431
+
432
+
433
+ # --------------------------------------------------------------------
434
+
435
+
436
+ def _save(im: Image.Image, fp: IO[bytes], filename: str | bytes, eps: int = 1) -> None:
437
+ """EPS Writer for the Python Imaging Library."""
438
+
439
+ # make sure image data is available
440
+ im.load()
441
+
442
+ # determine PostScript image mode
443
+ if im.mode == "L":
444
+ operator = (8, 1, b"image")
445
+ elif im.mode == "RGB":
446
+ operator = (8, 3, b"false 3 colorimage")
447
+ elif im.mode == "CMYK":
448
+ operator = (8, 4, b"false 4 colorimage")
449
+ else:
450
+ msg = "image mode is not supported"
451
+ raise ValueError(msg)
452
+
453
+ if eps:
454
+ # write EPS header
455
+ fp.write(b"%!PS-Adobe-3.0 EPSF-3.0\n")
456
+ fp.write(b"%%Creator: PIL 0.1 EpsEncode\n")
457
+ # fp.write("%%CreationDate: %s"...)
458
+ fp.write(b"%%%%BoundingBox: 0 0 %d %d\n" % im.size)
459
+ fp.write(b"%%Pages: 1\n")
460
+ fp.write(b"%%EndComments\n")
461
+ fp.write(b"%%Page: 1 1\n")
462
+ fp.write(b"%%ImageData: %d %d " % im.size)
463
+ fp.write(b'%d %d 0 1 1 "%s"\n' % operator)
464
+
465
+ # image header
466
+ fp.write(b"gsave\n")
467
+ fp.write(b"10 dict begin\n")
468
+ fp.write(b"/buf %d string def\n" % (im.size[0] * operator[1]))
469
+ fp.write(b"%d %d scale\n" % im.size)
470
+ fp.write(b"%d %d 8\n" % im.size) # <= bits
471
+ fp.write(b"[%d 0 0 -%d 0 %d]\n" % (im.size[0], im.size[1], im.size[1]))
472
+ fp.write(b"{ currentfile buf readhexstring pop } bind\n")
473
+ fp.write(operator[2] + b"\n")
474
+ if hasattr(fp, "flush"):
475
+ fp.flush()
476
+
477
+ ImageFile._save(im, fp, [ImageFile._Tile("eps", (0, 0, *im.size))])
478
+
479
+ fp.write(b"\n%%%%EndBinary\n")
480
+ fp.write(b"grestore end\n")
481
+ if hasattr(fp, "flush"):
482
+ fp.flush()
483
+
484
+
485
+ # --------------------------------------------------------------------
486
+
487
+
488
+ Image.register_open(EpsImageFile.format, EpsImageFile, _accept)
489
+
490
+ Image.register_save(EpsImageFile.format, _save)
491
+
492
+ Image.register_extensions(EpsImageFile.format, [".ps", ".eps"])
493
+
494
+ Image.register_mime(EpsImageFile.format, "application/postscript")
hug/lib/python3.12/site-packages/PIL/ExifTags.py ADDED
@@ -0,0 +1,383 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # EXIF tags
6
+ #
7
+ # Copyright (c) 2003 by Secret Labs AB
8
+ #
9
+ # See the README file for information on usage and redistribution.
10
+ #
11
+
12
+ """
13
+ This module provides constants and clear-text names for various
14
+ well-known EXIF tags.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from enum import IntEnum
20
+
21
+
22
+ class Base(IntEnum):
23
+ # possibly incomplete
24
+ InteropIndex = 0x0001
25
+ ProcessingSoftware = 0x000B
26
+ NewSubfileType = 0x00FE
27
+ SubfileType = 0x00FF
28
+ ImageWidth = 0x0100
29
+ ImageLength = 0x0101
30
+ BitsPerSample = 0x0102
31
+ Compression = 0x0103
32
+ PhotometricInterpretation = 0x0106
33
+ Thresholding = 0x0107
34
+ CellWidth = 0x0108
35
+ CellLength = 0x0109
36
+ FillOrder = 0x010A
37
+ DocumentName = 0x010D
38
+ ImageDescription = 0x010E
39
+ Make = 0x010F
40
+ Model = 0x0110
41
+ StripOffsets = 0x0111
42
+ Orientation = 0x0112
43
+ SamplesPerPixel = 0x0115
44
+ RowsPerStrip = 0x0116
45
+ StripByteCounts = 0x0117
46
+ MinSampleValue = 0x0118
47
+ MaxSampleValue = 0x0119
48
+ XResolution = 0x011A
49
+ YResolution = 0x011B
50
+ PlanarConfiguration = 0x011C
51
+ PageName = 0x011D
52
+ FreeOffsets = 0x0120
53
+ FreeByteCounts = 0x0121
54
+ GrayResponseUnit = 0x0122
55
+ GrayResponseCurve = 0x0123
56
+ T4Options = 0x0124
57
+ T6Options = 0x0125
58
+ ResolutionUnit = 0x0128
59
+ PageNumber = 0x0129
60
+ TransferFunction = 0x012D
61
+ Software = 0x0131
62
+ DateTime = 0x0132
63
+ Artist = 0x013B
64
+ HostComputer = 0x013C
65
+ Predictor = 0x013D
66
+ WhitePoint = 0x013E
67
+ PrimaryChromaticities = 0x013F
68
+ ColorMap = 0x0140
69
+ HalftoneHints = 0x0141
70
+ TileWidth = 0x0142
71
+ TileLength = 0x0143
72
+ TileOffsets = 0x0144
73
+ TileByteCounts = 0x0145
74
+ SubIFDs = 0x014A
75
+ InkSet = 0x014C
76
+ InkNames = 0x014D
77
+ NumberOfInks = 0x014E
78
+ DotRange = 0x0150
79
+ TargetPrinter = 0x0151
80
+ ExtraSamples = 0x0152
81
+ SampleFormat = 0x0153
82
+ SMinSampleValue = 0x0154
83
+ SMaxSampleValue = 0x0155
84
+ TransferRange = 0x0156
85
+ ClipPath = 0x0157
86
+ XClipPathUnits = 0x0158
87
+ YClipPathUnits = 0x0159
88
+ Indexed = 0x015A
89
+ JPEGTables = 0x015B
90
+ OPIProxy = 0x015F
91
+ JPEGProc = 0x0200
92
+ JpegIFOffset = 0x0201
93
+ JpegIFByteCount = 0x0202
94
+ JpegRestartInterval = 0x0203
95
+ JpegLosslessPredictors = 0x0205
96
+ JpegPointTransforms = 0x0206
97
+ JpegQTables = 0x0207
98
+ JpegDCTables = 0x0208
99
+ JpegACTables = 0x0209
100
+ YCbCrCoefficients = 0x0211
101
+ YCbCrSubSampling = 0x0212
102
+ YCbCrPositioning = 0x0213
103
+ ReferenceBlackWhite = 0x0214
104
+ XMLPacket = 0x02BC
105
+ RelatedImageFileFormat = 0x1000
106
+ RelatedImageWidth = 0x1001
107
+ RelatedImageLength = 0x1002
108
+ Rating = 0x4746
109
+ RatingPercent = 0x4749
110
+ ImageID = 0x800D
111
+ CFARepeatPatternDim = 0x828D
112
+ BatteryLevel = 0x828F
113
+ Copyright = 0x8298
114
+ ExposureTime = 0x829A
115
+ FNumber = 0x829D
116
+ IPTCNAA = 0x83BB
117
+ ImageResources = 0x8649
118
+ ExifOffset = 0x8769
119
+ InterColorProfile = 0x8773
120
+ ExposureProgram = 0x8822
121
+ SpectralSensitivity = 0x8824
122
+ GPSInfo = 0x8825
123
+ ISOSpeedRatings = 0x8827
124
+ OECF = 0x8828
125
+ Interlace = 0x8829
126
+ TimeZoneOffset = 0x882A
127
+ SelfTimerMode = 0x882B
128
+ SensitivityType = 0x8830
129
+ StandardOutputSensitivity = 0x8831
130
+ RecommendedExposureIndex = 0x8832
131
+ ISOSpeed = 0x8833
132
+ ISOSpeedLatitudeyyy = 0x8834
133
+ ISOSpeedLatitudezzz = 0x8835
134
+ ExifVersion = 0x9000
135
+ DateTimeOriginal = 0x9003
136
+ DateTimeDigitized = 0x9004
137
+ OffsetTime = 0x9010
138
+ OffsetTimeOriginal = 0x9011
139
+ OffsetTimeDigitized = 0x9012
140
+ ComponentsConfiguration = 0x9101
141
+ CompressedBitsPerPixel = 0x9102
142
+ ShutterSpeedValue = 0x9201
143
+ ApertureValue = 0x9202
144
+ BrightnessValue = 0x9203
145
+ ExposureBiasValue = 0x9204
146
+ MaxApertureValue = 0x9205
147
+ SubjectDistance = 0x9206
148
+ MeteringMode = 0x9207
149
+ LightSource = 0x9208
150
+ Flash = 0x9209
151
+ FocalLength = 0x920A
152
+ Noise = 0x920D
153
+ ImageNumber = 0x9211
154
+ SecurityClassification = 0x9212
155
+ ImageHistory = 0x9213
156
+ TIFFEPStandardID = 0x9216
157
+ MakerNote = 0x927C
158
+ UserComment = 0x9286
159
+ SubsecTime = 0x9290
160
+ SubsecTimeOriginal = 0x9291
161
+ SubsecTimeDigitized = 0x9292
162
+ AmbientTemperature = 0x9400
163
+ Humidity = 0x9401
164
+ Pressure = 0x9402
165
+ WaterDepth = 0x9403
166
+ Acceleration = 0x9404
167
+ CameraElevationAngle = 0x9405
168
+ XPTitle = 0x9C9B
169
+ XPComment = 0x9C9C
170
+ XPAuthor = 0x9C9D
171
+ XPKeywords = 0x9C9E
172
+ XPSubject = 0x9C9F
173
+ FlashPixVersion = 0xA000
174
+ ColorSpace = 0xA001
175
+ ExifImageWidth = 0xA002
176
+ ExifImageHeight = 0xA003
177
+ RelatedSoundFile = 0xA004
178
+ ExifInteroperabilityOffset = 0xA005
179
+ FlashEnergy = 0xA20B
180
+ SpatialFrequencyResponse = 0xA20C
181
+ FocalPlaneXResolution = 0xA20E
182
+ FocalPlaneYResolution = 0xA20F
183
+ FocalPlaneResolutionUnit = 0xA210
184
+ SubjectLocation = 0xA214
185
+ ExposureIndex = 0xA215
186
+ SensingMethod = 0xA217
187
+ FileSource = 0xA300
188
+ SceneType = 0xA301
189
+ CFAPattern = 0xA302
190
+ CustomRendered = 0xA401
191
+ ExposureMode = 0xA402
192
+ WhiteBalance = 0xA403
193
+ DigitalZoomRatio = 0xA404
194
+ FocalLengthIn35mmFilm = 0xA405
195
+ SceneCaptureType = 0xA406
196
+ GainControl = 0xA407
197
+ Contrast = 0xA408
198
+ Saturation = 0xA409
199
+ Sharpness = 0xA40A
200
+ DeviceSettingDescription = 0xA40B
201
+ SubjectDistanceRange = 0xA40C
202
+ ImageUniqueID = 0xA420
203
+ CameraOwnerName = 0xA430
204
+ BodySerialNumber = 0xA431
205
+ LensSpecification = 0xA432
206
+ LensMake = 0xA433
207
+ LensModel = 0xA434
208
+ LensSerialNumber = 0xA435
209
+ CompositeImage = 0xA460
210
+ CompositeImageCount = 0xA461
211
+ CompositeImageExposureTimes = 0xA462
212
+ Gamma = 0xA500
213
+ PrintImageMatching = 0xC4A5
214
+ DNGVersion = 0xC612
215
+ DNGBackwardVersion = 0xC613
216
+ UniqueCameraModel = 0xC614
217
+ LocalizedCameraModel = 0xC615
218
+ CFAPlaneColor = 0xC616
219
+ CFALayout = 0xC617
220
+ LinearizationTable = 0xC618
221
+ BlackLevelRepeatDim = 0xC619
222
+ BlackLevel = 0xC61A
223
+ BlackLevelDeltaH = 0xC61B
224
+ BlackLevelDeltaV = 0xC61C
225
+ WhiteLevel = 0xC61D
226
+ DefaultScale = 0xC61E
227
+ DefaultCropOrigin = 0xC61F
228
+ DefaultCropSize = 0xC620
229
+ ColorMatrix1 = 0xC621
230
+ ColorMatrix2 = 0xC622
231
+ CameraCalibration1 = 0xC623
232
+ CameraCalibration2 = 0xC624
233
+ ReductionMatrix1 = 0xC625
234
+ ReductionMatrix2 = 0xC626
235
+ AnalogBalance = 0xC627
236
+ AsShotNeutral = 0xC628
237
+ AsShotWhiteXY = 0xC629
238
+ BaselineExposure = 0xC62A
239
+ BaselineNoise = 0xC62B
240
+ BaselineSharpness = 0xC62C
241
+ BayerGreenSplit = 0xC62D
242
+ LinearResponseLimit = 0xC62E
243
+ CameraSerialNumber = 0xC62F
244
+ LensInfo = 0xC630
245
+ ChromaBlurRadius = 0xC631
246
+ AntiAliasStrength = 0xC632
247
+ ShadowScale = 0xC633
248
+ DNGPrivateData = 0xC634
249
+ MakerNoteSafety = 0xC635
250
+ CalibrationIlluminant1 = 0xC65A
251
+ CalibrationIlluminant2 = 0xC65B
252
+ BestQualityScale = 0xC65C
253
+ RawDataUniqueID = 0xC65D
254
+ OriginalRawFileName = 0xC68B
255
+ OriginalRawFileData = 0xC68C
256
+ ActiveArea = 0xC68D
257
+ MaskedAreas = 0xC68E
258
+ AsShotICCProfile = 0xC68F
259
+ AsShotPreProfileMatrix = 0xC690
260
+ CurrentICCProfile = 0xC691
261
+ CurrentPreProfileMatrix = 0xC692
262
+ ColorimetricReference = 0xC6BF
263
+ CameraCalibrationSignature = 0xC6F3
264
+ ProfileCalibrationSignature = 0xC6F4
265
+ AsShotProfileName = 0xC6F6
266
+ NoiseReductionApplied = 0xC6F7
267
+ ProfileName = 0xC6F8
268
+ ProfileHueSatMapDims = 0xC6F9
269
+ ProfileHueSatMapData1 = 0xC6FA
270
+ ProfileHueSatMapData2 = 0xC6FB
271
+ ProfileToneCurve = 0xC6FC
272
+ ProfileEmbedPolicy = 0xC6FD
273
+ ProfileCopyright = 0xC6FE
274
+ ForwardMatrix1 = 0xC714
275
+ ForwardMatrix2 = 0xC715
276
+ PreviewApplicationName = 0xC716
277
+ PreviewApplicationVersion = 0xC717
278
+ PreviewSettingsName = 0xC718
279
+ PreviewSettingsDigest = 0xC719
280
+ PreviewColorSpace = 0xC71A
281
+ PreviewDateTime = 0xC71B
282
+ RawImageDigest = 0xC71C
283
+ OriginalRawFileDigest = 0xC71D
284
+ SubTileBlockSize = 0xC71E
285
+ RowInterleaveFactor = 0xC71F
286
+ ProfileLookTableDims = 0xC725
287
+ ProfileLookTableData = 0xC726
288
+ OpcodeList1 = 0xC740
289
+ OpcodeList2 = 0xC741
290
+ OpcodeList3 = 0xC74E
291
+ NoiseProfile = 0xC761
292
+ FrameRate = 0xC764
293
+
294
+
295
+ """Maps EXIF tags to tag names."""
296
+ TAGS = {
297
+ **{i.value: i.name for i in Base},
298
+ 0x920C: "SpatialFrequencyResponse",
299
+ 0x9214: "SubjectLocation",
300
+ 0x9215: "ExposureIndex",
301
+ 0x828E: "CFAPattern",
302
+ 0x920B: "FlashEnergy",
303
+ 0x9216: "TIFF/EPStandardID",
304
+ }
305
+
306
+
307
+ class GPS(IntEnum):
308
+ GPSVersionID = 0x00
309
+ GPSLatitudeRef = 0x01
310
+ GPSLatitude = 0x02
311
+ GPSLongitudeRef = 0x03
312
+ GPSLongitude = 0x04
313
+ GPSAltitudeRef = 0x05
314
+ GPSAltitude = 0x06
315
+ GPSTimeStamp = 0x07
316
+ GPSSatellites = 0x08
317
+ GPSStatus = 0x09
318
+ GPSMeasureMode = 0x0A
319
+ GPSDOP = 0x0B
320
+ GPSSpeedRef = 0x0C
321
+ GPSSpeed = 0x0D
322
+ GPSTrackRef = 0x0E
323
+ GPSTrack = 0x0F
324
+ GPSImgDirectionRef = 0x10
325
+ GPSImgDirection = 0x11
326
+ GPSMapDatum = 0x12
327
+ GPSDestLatitudeRef = 0x13
328
+ GPSDestLatitude = 0x14
329
+ GPSDestLongitudeRef = 0x15
330
+ GPSDestLongitude = 0x16
331
+ GPSDestBearingRef = 0x17
332
+ GPSDestBearing = 0x18
333
+ GPSDestDistanceRef = 0x19
334
+ GPSDestDistance = 0x1A
335
+ GPSProcessingMethod = 0x1B
336
+ GPSAreaInformation = 0x1C
337
+ GPSDateStamp = 0x1D
338
+ GPSDifferential = 0x1E
339
+ GPSHPositioningError = 0x1F
340
+
341
+
342
+ """Maps EXIF GPS tags to tag names."""
343
+ GPSTAGS = {i.value: i.name for i in GPS}
344
+
345
+
346
+ class Interop(IntEnum):
347
+ InteropIndex = 0x0001
348
+ InteropVersion = 0x0002
349
+ RelatedImageFileFormat = 0x1000
350
+ RelatedImageWidth = 0x1001
351
+ RelatedImageHeight = 0x1002
352
+
353
+
354
+ class IFD(IntEnum):
355
+ Exif = 0x8769
356
+ GPSInfo = 0x8825
357
+ MakerNote = 0x927C
358
+ Interop = 0xA005
359
+ IFD1 = -1
360
+
361
+
362
+ class LightSource(IntEnum):
363
+ Unknown = 0x00
364
+ Daylight = 0x01
365
+ Fluorescent = 0x02
366
+ Tungsten = 0x03
367
+ Flash = 0x04
368
+ Fine = 0x09
369
+ Cloudy = 0x0A
370
+ Shade = 0x0B
371
+ DaylightFluorescent = 0x0C
372
+ DayWhiteFluorescent = 0x0D
373
+ CoolWhiteFluorescent = 0x0E
374
+ WhiteFluorescent = 0x0F
375
+ StandardLightA = 0x11
376
+ StandardLightB = 0x12
377
+ StandardLightC = 0x13
378
+ D55 = 0x14
379
+ D65 = 0x15
380
+ D75 = 0x16
381
+ D50 = 0x17
382
+ ISO = 0x18
383
+ Other = 0xFF
hug/lib/python3.12/site-packages/PIL/FitsImagePlugin.py ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library
3
+ # $Id$
4
+ #
5
+ # FITS file handling
6
+ #
7
+ # Copyright (c) 1998-2003 by Fredrik Lundh
8
+ #
9
+ # See the README file for information on usage and redistribution.
10
+ #
11
+ from __future__ import annotations
12
+
13
+ __lazy_modules__ = {"gzip", "math", "zlib"}
14
+
15
+ import gzip
16
+ import math
17
+ import zlib
18
+
19
+ from . import Image, ImageFile
20
+
21
+
22
+ def _accept(prefix: bytes) -> bool:
23
+ return prefix.startswith(b"SIMPLE")
24
+
25
+
26
+ class FitsImageFile(ImageFile.ImageFile):
27
+ format = "FITS"
28
+ format_description = "FITS"
29
+
30
+ def _open(self) -> None:
31
+ assert self.fp is not None
32
+
33
+ headers: dict[bytes, bytes] = {}
34
+ header_in_progress = False
35
+ decoder_name = ""
36
+ while True:
37
+ header = self.fp.read(80)
38
+ if not header:
39
+ msg = "Truncated FITS file"
40
+ raise OSError(msg)
41
+ keyword = header[:8].strip()
42
+ if keyword in (b"SIMPLE", b"XTENSION"):
43
+ header_in_progress = True
44
+ elif headers and not header_in_progress:
45
+ # This is now a data unit
46
+ break
47
+ elif keyword == b"END":
48
+ # Seek to the end of the header unit
49
+ self.fp.seek(math.ceil(self.fp.tell() / 2880) * 2880)
50
+ if not decoder_name:
51
+ decoder_name, offset, args = self._parse_headers(headers)
52
+
53
+ header_in_progress = False
54
+ continue
55
+
56
+ if decoder_name:
57
+ # Keep going to read past the headers
58
+ continue
59
+
60
+ value = header[8:].split(b"/")[0].strip()
61
+ if value.startswith(b"="):
62
+ value = value[1:].strip()
63
+ if not headers and (not _accept(keyword) or value != b"T"):
64
+ msg = "Not a FITS file"
65
+ raise SyntaxError(msg)
66
+ headers[keyword] = value
67
+
68
+ if not decoder_name:
69
+ msg = "No image data"
70
+ raise ValueError(msg)
71
+
72
+ offset += self.fp.tell() - 80
73
+ self.tile = [ImageFile._Tile(decoder_name, (0, 0, *self.size), offset, args)]
74
+
75
+ def _get_size(
76
+ self, headers: dict[bytes, bytes], prefix: bytes
77
+ ) -> tuple[int, int] | None:
78
+ naxis = int(headers[prefix + b"NAXIS"])
79
+ if naxis == 0:
80
+ return None
81
+
82
+ if naxis == 1:
83
+ return 1, int(headers[prefix + b"NAXIS1"])
84
+ else:
85
+ return int(headers[prefix + b"NAXIS1"]), int(headers[prefix + b"NAXIS2"])
86
+
87
+ def _parse_headers(
88
+ self, headers: dict[bytes, bytes]
89
+ ) -> tuple[str, int, tuple[str | int, ...]]:
90
+ prefix = b""
91
+ decoder_name = "raw"
92
+ offset = 0
93
+ if (
94
+ headers.get(b"XTENSION") == b"'BINTABLE'"
95
+ and headers.get(b"ZIMAGE") == b"T"
96
+ and headers[b"ZCMPTYPE"] == b"'GZIP_1 '"
97
+ ):
98
+ no_prefix_size = self._get_size(headers, prefix) or (0, 0)
99
+ number_of_bits = int(headers[b"BITPIX"])
100
+ offset = no_prefix_size[0] * no_prefix_size[1] * (number_of_bits // 8)
101
+
102
+ prefix = b"Z"
103
+ decoder_name = "fits_gzip"
104
+
105
+ size = self._get_size(headers, prefix)
106
+ if not size:
107
+ return "", 0, ()
108
+
109
+ self._size = size
110
+
111
+ number_of_bits = int(headers[prefix + b"BITPIX"])
112
+ if number_of_bits == 8:
113
+ self._mode = rawmode = "L"
114
+ elif number_of_bits == 16:
115
+ self._mode = "I;16"
116
+ rawmode = "I;16B"
117
+ elif number_of_bits == 32:
118
+ self._mode = "I"
119
+ rawmode = "I;32B"
120
+ elif number_of_bits == -32 or (decoder_name == "raw" and number_of_bits == -64):
121
+ self._mode = "F"
122
+ rawmode = f"F;{number_of_bits * -1}BF"
123
+ else:
124
+ msg = "Unsupported number of bits"
125
+ raise OSError(msg)
126
+
127
+ args: tuple[str | int, ...]
128
+ if decoder_name == "raw":
129
+ args = (rawmode, 0, -1)
130
+ else:
131
+ args = (number_of_bits,)
132
+ return decoder_name, offset, args
133
+
134
+
135
+ class FitsGzipDecoder(ImageFile.PyDecoder):
136
+ _pulls_fd = True
137
+
138
+ def decode(self, buffer: Image.DecoderInput) -> tuple[int, int]:
139
+ assert self.fd is not None
140
+ with gzip.open(self.fd) as fp:
141
+ try:
142
+ value = fp.read(self.state.xsize * self.state.ysize * 4)
143
+ except EOFError as e:
144
+ msg = "Truncated FITS gzip data"
145
+ raise OSError(msg) from e
146
+ except zlib.error as e:
147
+ msg = "Corrupt FITS gzip data"
148
+ raise OSError(msg) from e
149
+
150
+ rows = []
151
+ offset = 0
152
+ number_of_bytes = abs(self.args[0]) // 8
153
+ for y in range(self.state.ysize):
154
+ row = bytearray()
155
+ for x in range(self.state.xsize):
156
+ row += value[
157
+ offset + 4 - 1 : offset + (4 - number_of_bytes) - 1 : -1
158
+ ]
159
+ offset += 4
160
+ rows.append(row)
161
+ self.set_as_raw(bytes([pixel for row in rows[::-1] for pixel in row]))
162
+ return -1, 0
163
+
164
+
165
+ # --------------------------------------------------------------------
166
+ # Registry
167
+
168
+ Image.register_open(FitsImageFile.format, FitsImageFile, _accept)
169
+ Image.register_decoder("fits_gzip", FitsGzipDecoder)
170
+
171
+ Image.register_extensions(FitsImageFile.format, [".fit", ".fits"])
hug/lib/python3.12/site-packages/PIL/FliImagePlugin.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # FLI/FLC file handling.
6
+ #
7
+ # History:
8
+ # 95-09-01 fl Created
9
+ # 97-01-03 fl Fixed parser, setup decoder tile
10
+ # 98-07-15 fl Renamed offset attribute to avoid name clash
11
+ #
12
+ # Copyright (c) Secret Labs AB 1997-98.
13
+ # Copyright (c) Fredrik Lundh 1995-97.
14
+ #
15
+ # See the README file for information on usage and redistribution.
16
+ #
17
+ from __future__ import annotations
18
+
19
+ __lazy_modules__ = {"PIL._binary", "PIL._util"}
20
+
21
+ import os
22
+
23
+ from . import Image, ImageFile, ImagePalette
24
+ from ._binary import i16le as i16
25
+ from ._binary import i32le as i32
26
+ from ._util import DeferredError
27
+
28
+ #
29
+ # decoder
30
+
31
+
32
+ def _accept(prefix: bytes) -> bool:
33
+ return (
34
+ len(prefix) >= 16
35
+ and i16(prefix, 4) in [0xAF11, 0xAF12]
36
+ and i16(prefix, 14) in [0, 3] # flags
37
+ )
38
+
39
+
40
+ ##
41
+ # Image plugin for the FLI/FLC animation format. Use the <b>seek</b>
42
+ # method to load individual frames.
43
+
44
+
45
+ class FliImageFile(ImageFile.ImageFile):
46
+ format = "FLI"
47
+ format_description = "Autodesk FLI/FLC Animation"
48
+ _close_exclusive_fp_after_loading = False
49
+
50
+ def _open(self) -> None:
51
+ # HEAD
52
+ assert self.fp is not None
53
+ s = self.fp.read(128)
54
+ if not (
55
+ _accept(s)
56
+ and s[20:22] == b"\x00" * 2
57
+ and s[42:80] == b"\x00" * 38
58
+ and s[88:] == b"\x00" * 40
59
+ ):
60
+ msg = "not an FLI/FLC file"
61
+ raise SyntaxError(msg)
62
+
63
+ # frames
64
+ self.n_frames = i16(s, 6)
65
+ self.is_animated = self.n_frames > 1
66
+
67
+ # image characteristics
68
+ self._mode = "P"
69
+ self._size = i16(s, 8), i16(s, 10)
70
+
71
+ # animation speed
72
+ duration = i32(s, 16)
73
+ magic = i16(s, 4)
74
+ if magic == 0xAF11:
75
+ duration = (duration * 1000) // 70
76
+ self.info["duration"] = duration
77
+
78
+ # look for palette
79
+ palette = [(a, a, a) for a in range(256)]
80
+
81
+ s = self.fp.read(16)
82
+
83
+ self.__offset = 128
84
+
85
+ if i16(s, 4) == 0xF100:
86
+ # prefix chunk; ignore it
87
+ self.fp.seek(self.__offset + i32(s))
88
+ s = self.fp.read(16)
89
+
90
+ if i16(s, 4) == 0xF1FA:
91
+ # look for palette chunk
92
+ number_of_subchunks = i16(s, 6)
93
+ chunk_size: int | None = None
94
+ for _ in range(number_of_subchunks):
95
+ if chunk_size is not None:
96
+ self.fp.seek(chunk_size - 6, os.SEEK_CUR)
97
+ s = self.fp.read(6)
98
+ chunk_type = i16(s, 4)
99
+ if chunk_type in (4, 11):
100
+ self._palette(palette, 2 if chunk_type == 11 else 0)
101
+ break
102
+ chunk_size = i32(s)
103
+ if not chunk_size:
104
+ break
105
+
106
+ self.palette = ImagePalette.raw("RGB", tuple(v for rgb in palette for v in rgb))
107
+
108
+ # set things up to decode first frame
109
+ self.__frame = -1
110
+ self._fp = self.fp
111
+ self.__rewind = self.fp.tell()
112
+ self.seek(0)
113
+
114
+ def _palette(self, palette: list[tuple[int, int, int]], shift: int) -> None:
115
+ # load palette
116
+ i = 0
117
+ assert self.fp is not None
118
+ for e in range(i16(self.fp.read(2))):
119
+ s = self.fp.read(2)
120
+ i += s[0]
121
+ n = s[1]
122
+ if n == 0:
123
+ n = 256
124
+ if i + n > 256:
125
+ msg = "too much palette data"
126
+ raise ValueError(msg)
127
+ s = self.fp.read(n * 3)
128
+ for n in range(0, len(s), 3):
129
+ r = s[n] << shift
130
+ g = s[n + 1] << shift
131
+ b = s[n + 2] << shift
132
+ if r > 255 or g > 255 or b > 255:
133
+ msg = "palette data out of range"
134
+ raise ValueError(msg)
135
+ palette[i] = (r, g, b)
136
+ i += 1
137
+
138
+ def seek(self, frame: int) -> None:
139
+ if not self._seek_check(frame):
140
+ return
141
+ if frame < self.__frame:
142
+ self._seek(0)
143
+
144
+ for f in range(self.__frame + 1, frame + 1):
145
+ self._seek(f)
146
+
147
+ def _seek(self, frame: int) -> None:
148
+ if isinstance(self._fp, DeferredError):
149
+ raise self._fp.ex
150
+ if frame == 0:
151
+ self.__frame = -1
152
+ self._fp.seek(self.__rewind)
153
+ self.__offset = 128
154
+ else:
155
+ # ensure that the previous frame was loaded
156
+ self.load()
157
+
158
+ if frame != self.__frame + 1:
159
+ msg = f"cannot seek to frame {frame}"
160
+ raise ValueError(msg)
161
+ self.__frame = frame
162
+
163
+ # move to next frame
164
+ self.fp = self._fp
165
+ self.fp.seek(self.__offset)
166
+
167
+ s = self.fp.read(4)
168
+ if not s:
169
+ msg = "missing frame size"
170
+ raise EOFError(msg)
171
+
172
+ framesize = i32(s)
173
+
174
+ self.decodermaxblock = framesize
175
+ self.tile = [ImageFile._Tile("fli", (0, 0, *self.size), self.__offset)]
176
+
177
+ self.__offset += framesize
178
+
179
+ def tell(self) -> int:
180
+ return self.__frame
181
+
182
+
183
+ #
184
+ # registry
185
+
186
+ Image.register_open(FliImageFile.format, FliImageFile, _accept)
187
+
188
+ Image.register_extensions(FliImageFile.format, [".fli", ".flc"])
hug/lib/python3.12/site-packages/PIL/FontFile.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library
3
+ # $Id$
4
+ #
5
+ # base class for raster font file parsers
6
+ #
7
+ # history:
8
+ # 1997-06-05 fl created
9
+ # 1997-08-19 fl restrict image width
10
+ #
11
+ # Copyright (c) 1997-1998 by Secret Labs AB
12
+ # Copyright (c) 1997-1998 by Fredrik Lundh
13
+ #
14
+ # See the README file for information on usage and redistribution.
15
+ #
16
+ from __future__ import annotations
17
+
18
+ import os
19
+
20
+ from . import Image, ImageFont, _binary
21
+
22
+ TYPE_CHECKING = False
23
+ if TYPE_CHECKING:
24
+ from typing import BinaryIO
25
+
26
+ WIDTH = 800
27
+
28
+
29
+ def puti16(
30
+ fp: BinaryIO, values: tuple[int, int, int, int, int, int, int, int, int, int]
31
+ ) -> None:
32
+ """Write network order (big-endian) 16-bit sequence"""
33
+ for v in values:
34
+ if v < 0:
35
+ v += 65536
36
+ fp.write(_binary.o16be(v))
37
+
38
+
39
+ class FontFile:
40
+ """Base class for raster font file handlers."""
41
+
42
+ bitmap: Image.Image | None = None
43
+
44
+ def __init__(self) -> None:
45
+ self.info: dict[bytes, bytes | int] = {}
46
+ self.glyph: list[
47
+ tuple[
48
+ tuple[int, int],
49
+ tuple[int, int, int, int],
50
+ tuple[int, int, int, int],
51
+ Image.Image,
52
+ ]
53
+ | None
54
+ ] = [None] * 256
55
+
56
+ def __getitem__(self, ix: int) -> (
57
+ tuple[
58
+ tuple[int, int],
59
+ tuple[int, int, int, int],
60
+ tuple[int, int, int, int],
61
+ Image.Image,
62
+ ]
63
+ | None
64
+ ):
65
+ return self.glyph[ix]
66
+
67
+ def compile(self) -> None:
68
+ """Create metrics and bitmap"""
69
+
70
+ if self.bitmap:
71
+ return
72
+
73
+ # create bitmap large enough to hold all data
74
+ h = w = maxwidth = 0
75
+ lines = 1
76
+ for glyph in self.glyph:
77
+ if glyph:
78
+ d, dst, src, im = glyph
79
+ h = max(h, src[3] - src[1])
80
+ w += src[2] - src[0]
81
+ if w > WIDTH:
82
+ lines += 1
83
+ w = src[2] - src[0]
84
+ maxwidth = max(maxwidth, w)
85
+
86
+ xsize = maxwidth
87
+ ysize = lines * h
88
+
89
+ if xsize == 0 and ysize == 0:
90
+ return
91
+
92
+ self.ysize = h
93
+
94
+ # paste glyphs into bitmap
95
+ Image._decompression_bomb_check((xsize, ysize))
96
+ self.bitmap = Image.new("1", (xsize, ysize))
97
+ self.metrics: list[
98
+ tuple[tuple[int, int], tuple[int, int, int, int], tuple[int, int, int, int]]
99
+ | None
100
+ ] = [None] * 256
101
+ x = y = 0
102
+ for i in range(256):
103
+ glyph = self[i]
104
+ if glyph:
105
+ d, dst, src, im = glyph
106
+ xx = src[2] - src[0]
107
+ x0, y0 = x, y
108
+ x = x + xx
109
+ if x > WIDTH:
110
+ x, y = 0, y + h
111
+ x0, y0 = x, y
112
+ x = xx
113
+ s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
114
+ self.bitmap.paste(im.crop(src), s)
115
+ self.metrics[i] = d, dst, s
116
+
117
+ def _encode_metrics(self) -> bytes:
118
+ values: list[int] = []
119
+ for i in range(256):
120
+ m = self.metrics[i]
121
+ if m:
122
+ values.extend(m[0] + m[1] + m[2])
123
+ else:
124
+ values.extend((0,) * 10)
125
+
126
+ data = bytearray()
127
+ for v in values:
128
+ if v < 0:
129
+ v += 65536
130
+ data += _binary.o16be(v)
131
+ return bytes(data)
132
+
133
+ def save(self, filename: str | os.PathLike[str]) -> None:
134
+ """Save font"""
135
+
136
+ self.compile()
137
+
138
+ # font data
139
+ if not self.bitmap:
140
+ msg = "No bitmap created"
141
+ raise ValueError(msg)
142
+ self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")
143
+
144
+ # font metrics
145
+ with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
146
+ fp.write(b"PILfont\n")
147
+ fp.write(f";;;;;;{self.ysize};\n".encode("ascii")) # HACK!!!
148
+ fp.write(b"DATA\n")
149
+ fp.write(self._encode_metrics())
150
+
151
+ def to_imagefont(self) -> ImageFont.ImageFont:
152
+ """Convert to ImageFont"""
153
+
154
+ self.compile()
155
+
156
+ # font data
157
+ if not self.bitmap:
158
+ msg = "No bitmap created"
159
+ raise ValueError(msg)
160
+
161
+ imagefont = ImageFont.ImageFont()
162
+ imagefont._load(self.bitmap, self._encode_metrics())
163
+ return imagefont
hug/lib/python3.12/site-packages/PIL/FpxImagePlugin.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # THIS IS WORK IN PROGRESS
3
+ #
4
+ # The Python Imaging Library.
5
+ # $Id$
6
+ #
7
+ # FlashPix support for PIL
8
+ #
9
+ # History:
10
+ # 97-01-25 fl Created (reads uncompressed RGB images only)
11
+ #
12
+ # Copyright (c) Secret Labs AB 1997.
13
+ # Copyright (c) Fredrik Lundh 1997.
14
+ #
15
+ # See the README file for information on usage and redistribution.
16
+ #
17
+ from __future__ import annotations
18
+
19
+ __lazy_modules__ = {"PIL._binary"}
20
+
21
+ import olefile
22
+
23
+ from . import Image, ImageFile
24
+ from ._binary import i32le as i32
25
+
26
+ # we map from colour field tuples to (mode, rawmode) descriptors
27
+ MODES = {
28
+ # opacity
29
+ (0x00007FFE,): ("A", "L"),
30
+ # monochrome
31
+ (0x00010000,): ("L", "L"),
32
+ (0x00018000, 0x00017FFE): ("RGBA", "LA"),
33
+ # photo YCC
34
+ (0x00020000, 0x00020001, 0x00020002): ("RGB", "YCC;P"),
35
+ (0x00028000, 0x00028001, 0x00028002, 0x00027FFE): ("RGBA", "YCCA;P"),
36
+ # standard RGB (NIFRGB)
37
+ (0x00030000, 0x00030001, 0x00030002): ("RGB", "RGB"),
38
+ (0x00038000, 0x00038001, 0x00038002, 0x00037FFE): ("RGBA", "RGBA"),
39
+ }
40
+
41
+
42
+ #
43
+ # --------------------------------------------------------------------
44
+
45
+
46
+ def _accept(prefix: bytes) -> bool:
47
+ return prefix.startswith(olefile.MAGIC)
48
+
49
+
50
+ ##
51
+ # Image plugin for the FlashPix images.
52
+
53
+
54
+ class FpxImageFile(ImageFile.ImageFile):
55
+ format = "FPX"
56
+ format_description = "FlashPix"
57
+
58
+ def _open(self) -> None:
59
+ #
60
+ # read the OLE directory and see if this is a likely
61
+ # to be a FlashPix file
62
+
63
+ assert self.fp is not None
64
+ try:
65
+ self.ole = olefile.OleFileIO(self.fp)
66
+ except OSError as e:
67
+ msg = "not an FPX file; invalid OLE file"
68
+ raise SyntaxError(msg) from e
69
+
70
+ root = self.ole.root
71
+ if not root or root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B":
72
+ msg = "not an FPX file; bad root CLSID"
73
+ raise SyntaxError(msg)
74
+
75
+ self._open_index(1)
76
+
77
+ def _open_index(self, index: int = 1) -> None:
78
+ #
79
+ # get the Image Contents Property Set
80
+
81
+ prop = self.ole.getproperties(
82
+ [f"Data Object Store {index:06d}", "\005Image Contents"]
83
+ )
84
+
85
+ # size (highest resolution)
86
+
87
+ assert isinstance(prop[0x1000002], int)
88
+ assert isinstance(prop[0x1000003], int)
89
+ self._size = prop[0x1000002], prop[0x1000003]
90
+
91
+ size = max(self.size)
92
+ i = 1
93
+ while size > 64:
94
+ size = size // 2
95
+ i += 1
96
+ self.maxid = i - 1
97
+
98
+ # mode. instead of using a single field for this, flashpix
99
+ # requires you to specify the mode for each channel in each
100
+ # resolution subimage, and leaves it to the decoder to make
101
+ # sure that they all match. for now, we'll cheat and assume
102
+ # that this is always the case.
103
+
104
+ id = self.maxid << 16
105
+
106
+ s = prop[0x2000002 | id]
107
+
108
+ if not isinstance(s, bytes) or (bands := i32(s, 4)) > 4:
109
+ msg = "Invalid number of bands"
110
+ raise OSError(msg)
111
+
112
+ # note: for now, we ignore the "uncalibrated" flag
113
+ colors = tuple(i32(s, 8 + i * 4) & 0x7FFFFFFF for i in range(bands))
114
+
115
+ self._mode, self.rawmode = MODES[colors]
116
+
117
+ # load JPEG tables, if any
118
+ self.jpeg = {}
119
+ for i in range(256):
120
+ id = 0x3000001 | (i << 16)
121
+ if id in prop:
122
+ self.jpeg[i] = prop[id]
123
+
124
+ self._open_subimage(1, self.maxid)
125
+
126
+ def _open_subimage(self, index: int = 1, subimage: int = 0) -> None:
127
+ #
128
+ # setup tile descriptors for a given subimage
129
+
130
+ stream = [
131
+ f"Data Object Store {index:06d}",
132
+ f"Resolution {subimage:04d}",
133
+ "Subimage 0000 Header",
134
+ ]
135
+
136
+ fp = self.ole.openstream(stream)
137
+
138
+ # skip prefix
139
+ fp.read(28)
140
+
141
+ # header stream
142
+ s = fp.read(36)
143
+
144
+ size = i32(s, 4), i32(s, 8)
145
+ # tilecount = i32(s, 12)
146
+ xtile, ytile = i32(s, 16), i32(s, 20)
147
+ if xtile != 64 or ytile != 64:
148
+ msg = "Tile must be 64 pixels by 64 pixels"
149
+ raise ValueError(msg)
150
+
151
+ # channels = i32(s, 24)
152
+ offset = i32(s, 28)
153
+ length = i32(s, 32)
154
+
155
+ if size != self.size:
156
+ msg = "subimage mismatch"
157
+ raise OSError(msg)
158
+
159
+ # get tile descriptors
160
+ fp.seek(28 + offset)
161
+ s = fp.read(i32(s, 12) * length)
162
+
163
+ x = y = 0
164
+ xsize, ysize = size
165
+ self.tile = []
166
+
167
+ for i in range(0, len(s), length):
168
+ x1 = min(xsize, x + xtile)
169
+ y1 = min(ysize, y + ytile)
170
+
171
+ compression = i32(s, i + 8)
172
+
173
+ if compression == 0:
174
+ self.tile.append(
175
+ ImageFile._Tile(
176
+ "raw",
177
+ (x, y, x1, y1),
178
+ i32(s, i) + 28,
179
+ self.rawmode,
180
+ )
181
+ )
182
+
183
+ elif compression == 1:
184
+ # FIXME: the fill decoder is not implemented
185
+ self.tile.append(
186
+ ImageFile._Tile(
187
+ "fill",
188
+ (x, y, x1, y1),
189
+ i32(s, i) + 28,
190
+ (self.rawmode, s[12:16]),
191
+ )
192
+ )
193
+
194
+ elif compression == 2:
195
+ internal_color_conversion = s[14]
196
+ jpeg_tables = s[15]
197
+ rawmode = self.rawmode
198
+
199
+ if internal_color_conversion:
200
+ # The image is stored as usual (usually YCbCr).
201
+ if rawmode == "RGBA":
202
+ # For "RGBA", data is stored as YCbCrA based on
203
+ # negative RGB. The following trick works around
204
+ # this problem :
205
+ jpegmode, rawmode = "YCbCrK", "CMYK"
206
+ else:
207
+ jpegmode = None # let the decoder decide
208
+
209
+ else:
210
+ # The image is stored as defined by rawmode
211
+ jpegmode = rawmode
212
+
213
+ self.tile.append(
214
+ ImageFile._Tile(
215
+ "jpeg",
216
+ (x, y, x1, y1),
217
+ i32(s, i) + 28,
218
+ (rawmode, jpegmode),
219
+ )
220
+ )
221
+
222
+ # FIXME: jpeg tables are tile dependent; the prefix
223
+ # data must be placed in the tile descriptor itself!
224
+
225
+ if jpeg_tables:
226
+ self.tile_prefix = self.jpeg[jpeg_tables]
227
+
228
+ else:
229
+ msg = "unknown/invalid compression"
230
+ raise OSError(msg)
231
+
232
+ x += xtile
233
+ if x >= xsize:
234
+ x, y = 0, y + ytile
235
+ if y >= ysize:
236
+ break # isn't really required
237
+
238
+ assert self.fp is not None
239
+ self.stream = stream
240
+ self._fp = self.fp
241
+ self.fp = None
242
+
243
+ def load(self) -> Image.core.PixelAccess | None:
244
+ if not self.fp:
245
+ self.fp = self.ole.openstream(self.stream[:2] + ["Subimage 0000 Data"])
246
+
247
+ return ImageFile.ImageFile.load(self)
248
+
249
+ def close(self) -> None:
250
+ self.ole.close()
251
+ super().close()
252
+
253
+ def __exit__(self, *args: object) -> None:
254
+ self.ole.close()
255
+ super().__exit__()
256
+
257
+
258
+ #
259
+ # --------------------------------------------------------------------
260
+
261
+
262
+ Image.register_open(FpxImageFile.format, FpxImageFile, _accept)
263
+
264
+ Image.register_extension(FpxImageFile.format, ".fpx")
hug/lib/python3.12/site-packages/PIL/FtexImagePlugin.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ A Pillow loader for .ftc and .ftu files (FTEX)
3
+ Jerome Leclanche <jerome@leclan.ch>
4
+
5
+ The contents of this file are hereby released in the public domain (CC0)
6
+ Full text of the CC0 license:
7
+ https://creativecommons.org/publicdomain/zero/1.0/
8
+
9
+ Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001
10
+
11
+ The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a
12
+ packed custom format called FTEX. This file format uses file extensions FTC
13
+ and FTU.
14
+ * FTC files are compressed textures (using standard texture compression).
15
+ * FTU files are not compressed.
16
+ Texture File Format
17
+ The FTC and FTU texture files both use the same format. This
18
+ has the following structure:
19
+ {header}
20
+ {format_directory}
21
+ {data}
22
+ Where:
23
+ {header} = {
24
+ u32:magic,
25
+ u32:version,
26
+ u32:width,
27
+ u32:height,
28
+ u32:mipmap_count,
29
+ u32:format_count
30
+ }
31
+
32
+ * The "magic" number is "FTEX".
33
+ * "width" and "height" are the dimensions of the texture.
34
+ * "mipmap_count" is the number of mipmaps in the texture.
35
+ * "format_count" is the number of texture formats (different versions of the
36
+ same texture) in this file.
37
+
38
+ {format_directory} = format_count * { u32:format, u32:where }
39
+
40
+ The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB
41
+ uncompressed textures.
42
+ The texture data for a format starts at the position "where" in the file.
43
+
44
+ Each set of texture data in the file has the following structure:
45
+ {data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } }
46
+ * "mipmap_size" is the number of bytes in that mip level. For compressed
47
+ textures this is the size of the texture data compressed with DXT1. For 24 bit
48
+ uncompressed textures, this is 3 * width * height. Following this are the image
49
+ bytes for that mipmap level.
50
+
51
+ Note: All data is stored in little-Endian (Intel) byte order.
52
+ """
53
+
54
+ from __future__ import annotations
55
+
56
+ __lazy_modules__ = {"io", "struct"}
57
+
58
+ import struct
59
+ from enum import IntEnum
60
+ from io import BytesIO
61
+
62
+ from . import Image, ImageFile
63
+
64
+ MAGIC = b"FTEX"
65
+
66
+
67
+ class Format(IntEnum):
68
+ DXT1 = 0
69
+ UNCOMPRESSED = 1
70
+
71
+
72
+ class FtexImageFile(ImageFile.ImageFile):
73
+ format = "FTEX"
74
+ format_description = "Texture File Format (IW2:EOC)"
75
+
76
+ def _open(self) -> None:
77
+ assert self.fp is not None
78
+ if not _accept(self.fp.read(4)):
79
+ msg = "not an FTEX file"
80
+ raise SyntaxError(msg)
81
+ struct.unpack("<i", self.fp.read(4)) # version
82
+ self._size = struct.unpack("<2i", self.fp.read(8))
83
+ mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
84
+
85
+ # Only support single-format files.
86
+ # I don't know of any multi-format file.
87
+ if format_count != 1:
88
+ msg = f"Unsupported number of texture formats: {format_count}"
89
+ raise ValueError(msg)
90
+
91
+ format, where = struct.unpack("<2i", self.fp.read(8))
92
+ self.fp.seek(where)
93
+ (mipmap_size,) = struct.unpack("<i", self.fp.read(4))
94
+
95
+ data = self.fp.read(mipmap_size)
96
+
97
+ if format == Format.DXT1:
98
+ self._mode = "RGBA"
99
+ self.tile = [ImageFile._Tile("bcn", (0, 0, *self.size), 0, (1,))]
100
+ elif format == Format.UNCOMPRESSED:
101
+ self._mode = "RGB"
102
+ self.tile = [ImageFile._Tile("raw", (0, 0, *self.size), 0, "RGB")]
103
+ else:
104
+ msg = f"Invalid texture compression format: {repr(format)}"
105
+ raise ValueError(msg)
106
+
107
+ self.fp.close()
108
+ self.fp = BytesIO(data)
109
+
110
+ def load_seek(self, pos: int) -> None:
111
+ pass
112
+
113
+
114
+ def _accept(prefix: bytes) -> bool:
115
+ return prefix.startswith(MAGIC)
116
+
117
+
118
+ Image.register_open(FtexImageFile.format, FtexImageFile, _accept)
119
+ Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"])
hug/lib/python3.12/site-packages/PIL/GbrImagePlugin.py ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library
3
+ #
4
+ # load a GIMP brush file
5
+ #
6
+ # History:
7
+ # 96-03-14 fl Created
8
+ # 16-01-08 es Version 2
9
+ #
10
+ # Copyright (c) Secret Labs AB 1997.
11
+ # Copyright (c) Fredrik Lundh 1996.
12
+ # Copyright (c) Eric Soroos 2016.
13
+ #
14
+ # See the README file for information on usage and redistribution.
15
+ #
16
+ #
17
+ # See https://github.com/GNOME/gimp/blob/mainline/devel-docs/gbr.txt for
18
+ # format documentation.
19
+ #
20
+ # This code Interprets version 1 and 2 .gbr files.
21
+ # Version 1 files are obsolete, and should not be used for new
22
+ # brushes.
23
+ # Version 2 files are saved by GIMP v2.8 (at least)
24
+ # Version 3 files have a format specifier of 18 for 16bit floats in
25
+ # the color depth field. This is currently unsupported by Pillow.
26
+ from __future__ import annotations
27
+
28
+ __lazy_modules__ = {"PIL._binary"}
29
+
30
+ from . import Image, ImageFile
31
+ from ._binary import i32be as i32
32
+
33
+
34
+ def _accept(prefix: bytes) -> bool:
35
+ return len(prefix) >= 8 and i32(prefix, 0) >= 20 and i32(prefix, 4) in (1, 2)
36
+
37
+
38
+ ##
39
+ # Image plugin for the GIMP brush format.
40
+
41
+
42
+ class GbrImageFile(ImageFile.ImageFile):
43
+ format = "GBR"
44
+ format_description = "GIMP brush file"
45
+
46
+ def _open(self) -> None:
47
+ assert self.fp is not None
48
+ header_size = i32(self.fp.read(4))
49
+ if header_size < 20:
50
+ msg = "not a GIMP brush"
51
+ raise SyntaxError(msg)
52
+ version = i32(self.fp.read(4))
53
+ if version not in (1, 2):
54
+ msg = f"Unsupported GIMP brush version: {version}"
55
+ raise SyntaxError(msg)
56
+
57
+ width = i32(self.fp.read(4))
58
+ height = i32(self.fp.read(4))
59
+ if width == 0 or height == 0:
60
+ msg = "not a GIMP brush"
61
+ raise SyntaxError(msg)
62
+ color_depth = i32(self.fp.read(4))
63
+ if color_depth not in (1, 4):
64
+ msg = f"Unsupported GIMP brush color depth: {color_depth}"
65
+ raise SyntaxError(msg)
66
+
67
+ if version == 1:
68
+ comment_length = header_size - 20
69
+ else:
70
+ comment_length = header_size - 28
71
+ magic_number = self.fp.read(4)
72
+ if magic_number != b"GIMP":
73
+ msg = "not a GIMP brush, bad magic number"
74
+ raise SyntaxError(msg)
75
+ self.info["spacing"] = i32(self.fp.read(4))
76
+
77
+ self.info["comment"] = self.fp.read(comment_length)[:-1]
78
+
79
+ self._mode = "L" if color_depth == 1 else "RGBA"
80
+ self._size = width, height
81
+
82
+ # Image might not be small
83
+ Image._decompression_bomb_check(self.size)
84
+
85
+ def load(self) -> Image.core.PixelAccess | None:
86
+ if self._im is None:
87
+ assert self.fp is not None
88
+ self.im = Image.core.new(self.mode, self.size)
89
+
90
+ # Data is an uncompressed block of w * h * bytes/pixel
91
+ data_size = self.width * self.height * Image.getmodebands(self.mode)
92
+ self.frombytes(self.fp.read(data_size))
93
+ return Image.Image.load(self)
94
+
95
+
96
+ #
97
+ # registry
98
+
99
+
100
+ Image.register_open(GbrImageFile.format, GbrImageFile, _accept)
101
+ Image.register_extension(GbrImageFile.format, ".gbr")
hug/lib/python3.12/site-packages/PIL/GdImageFile.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # The Python Imaging Library.
3
+ # $Id$
4
+ #
5
+ # GD file handling
6
+ #
7
+ # History:
8
+ # 1996-04-12 fl Created
9
+ #
10
+ # Copyright (c) 1997 by Secret Labs AB.
11
+ # Copyright (c) 1996 by Fredrik Lundh.
12
+ #
13
+ # See the README file for information on usage and redistribution.
14
+ #
15
+
16
+
17
+ """
18
+ .. note::
19
+ This format cannot be automatically recognized, so the
20
+ class is not registered for use with :py:func:`PIL.Image.open()`. To open a
21
+ gd file, use the :py:func:`PIL.GdImageFile.open()` function instead.
22
+
23
+ .. warning::
24
+ THE GD FORMAT IS NOT DESIGNED FOR DATA INTERCHANGE. This
25
+ implementation is provided for convenience and demonstrational
26
+ purposes only.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ __lazy_modules__ = {"PIL._binary"}
32
+
33
+ from . import Image, ImageFile, ImagePalette, UnidentifiedImageError
34
+ from ._binary import i16be as i16
35
+ from ._binary import i32be as i32
36
+
37
+ TYPE_CHECKING = False
38
+ if TYPE_CHECKING:
39
+ from typing import IO
40
+
41
+ from ._typing import StrOrBytesPath
42
+
43
+
44
+ class GdImageFile(ImageFile.ImageFile):
45
+ """
46
+ Image plugin for the GD uncompressed format. Note that this format
47
+ is not supported by the standard :py:func:`PIL.Image.open()` function. To use
48
+ this plugin, you have to import the :py:mod:`PIL.GdImageFile` module and
49
+ use the :py:func:`PIL.GdImageFile.open()` function.
50
+ """
51
+
52
+ format = "GD"
53
+ format_description = "GD uncompressed images"
54
+
55
+ def _open(self) -> None:
56
+ # Header
57
+ assert self.fp is not None
58
+
59
+ s = self.fp.read(1037)
60
+
61
+ if i16(s) not in [65534, 65535]:
62
+ msg = "Not a valid GD 2.x .gd file"
63
+ raise SyntaxError(msg)
64
+
65
+ self._mode = "P"
66
+ self._size = i16(s, 2), i16(s, 4)
67
+ Image._decompression_bomb_check(self.size)
68
+
69
+ true_color = s[6]
70
+ true_color_offset = 2 if true_color else 0
71
+
72
+ # transparency index
73
+ tindex = i32(s, 7 + true_color_offset)
74
+ if tindex < 256:
75
+ self.info["transparency"] = tindex
76
+
77
+ self.palette = ImagePalette.raw(
78
+ "RGBX", s[7 + true_color_offset + 6 : 7 + true_color_offset + 6 + 256 * 4]
79
+ )
80
+
81
+ self.tile = [
82
+ ImageFile._Tile(
83
+ "raw",
84
+ (0, 0, *self.size),
85
+ 7 + true_color_offset + 6 + 256 * 4,
86
+ "L",
87
+ )
88
+ ]
89
+
90
+
91
+ def open(fp: StrOrBytesPath | IO[bytes], mode: str = "r") -> GdImageFile:
92
+ """
93
+ Load texture from a GD image file.
94
+
95
+ :param fp: GD file name, or an opened file handle.
96
+ :param mode: Optional mode. In this version, if the mode argument
97
+ is given, it must be "r".
98
+ :returns: An image instance.
99
+ :raises OSError: If the image could not be read.
100
+ """
101
+ if mode != "r":
102
+ msg = "bad mode"
103
+ raise ValueError(msg)
104
+
105
+ try:
106
+ return GdImageFile(fp)
107
+ except SyntaxError as e:
108
+ msg = "cannot identify this image file"
109
+ raise UnidentifiedImageError(msg) from e