Ssspirit qingxu98 commited on
Commit
fe515ee
0 Parent(s):

Duplicate from qingxu98/gpt-academic

Browse files

Co-authored-by: fuqingxu <qingxu98@users.noreply.huggingface.co>

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +5 -0
  2. .github/ISSUE_TEMPLATE/bug_report.yml +75 -0
  3. .github/ISSUE_TEMPLATE/feature_request.yml +28 -0
  4. .github/workflows/build-with-chatglm.yml +44 -0
  5. .github/workflows/build-with-jittorllms.yml +44 -0
  6. .github/workflows/build-with-latex.yml +44 -0
  7. .github/workflows/build-without-local-llms.yml +44 -0
  8. .gitignore +152 -0
  9. Dockerfile +34 -0
  10. LICENSE +674 -0
  11. README.md +382 -0
  12. app.py +261 -0
  13. check_proxy.py +169 -0
  14. colorful.py +61 -0
  15. config.py +253 -0
  16. core_functional.py +96 -0
  17. crazy_functional.py +564 -0
  18. crazy_functions/CodeInterpreter.py +231 -0
  19. crazy_functions/Langchain知识库.py +107 -0
  20. crazy_functions/Latex全文润色.py +243 -0
  21. crazy_functions/Latex全文翻译.py +175 -0
  22. crazy_functions/Latex输出PDF结果.py +300 -0
  23. crazy_functions/__init__.py +0 -0
  24. crazy_functions/chatglm微调工具.py +141 -0
  25. crazy_functions/crazy_functions_test.py +231 -0
  26. crazy_functions/crazy_utils.py +758 -0
  27. crazy_functions/json_fns/pydantic_io.py +111 -0
  28. crazy_functions/latex_fns/latex_actions.py +447 -0
  29. crazy_functions/latex_fns/latex_toolbox.py +459 -0
  30. crazy_functions/latex_utils.py +788 -0
  31. crazy_functions/live_audio/aliyunASR.py +129 -0
  32. crazy_functions/live_audio/audio_io.py +51 -0
  33. crazy_functions/pdf_fns/parse_pdf.py +30 -0
  34. crazy_functions/test_project/cpp/cppipc/buffer.cpp +87 -0
  35. crazy_functions/test_project/cpp/cppipc/ipc.cpp +701 -0
  36. crazy_functions/test_project/cpp/cppipc/policy.h +25 -0
  37. crazy_functions/test_project/cpp/cppipc/pool_alloc.cpp +17 -0
  38. crazy_functions/test_project/cpp/cppipc/prod_cons.h +433 -0
  39. crazy_functions/test_project/cpp/cppipc/queue.h +216 -0
  40. crazy_functions/test_project/cpp/cppipc/shm.cpp +103 -0
  41. crazy_functions/test_project/cpp/cppipc/waiter.h +83 -0
  42. crazy_functions/test_project/cpp/cppipc/来源 +3 -0
  43. crazy_functions/test_project/cpp/libJPG/jpgd.cpp +3276 -0
  44. crazy_functions/test_project/cpp/libJPG/jpgd.h +316 -0
  45. crazy_functions/test_project/cpp/libJPG/jpge.cpp +1049 -0
  46. crazy_functions/test_project/cpp/libJPG/jpge.h +172 -0
  47. crazy_functions/test_project/cpp/libJPG/来源 +3 -0
  48. crazy_functions/test_project/cpp/longcode/jpgd.cpp +3276 -0
  49. crazy_functions/test_project/cpp/longcode/jpge.cpp +1049 -0
  50. crazy_functions/test_project/cpp/longcode/prod_cons.h +433 -0
.gitattributes ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ *.h linguist-detectable=false
2
+ *.cpp linguist-detectable=false
3
+ *.tex linguist-detectable=false
4
+ *.cs linguist-detectable=false
5
+ *.tps linguist-detectable=false
.github/ISSUE_TEMPLATE/bug_report.yml ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Report Bug | 报告BUG
2
+ description: "Report bug"
3
+ title: "[Bug]: "
4
+ labels: []
5
+ body:
6
+ - type: dropdown
7
+ id: download
8
+ attributes:
9
+ label: Installation Method | 安装方法与平台
10
+ options:
11
+ - Please choose | 请选择
12
+ - Pip Install (I ignored requirements.txt)
13
+ - Pip Install (I used latest requirements.txt)
14
+ - Anaconda (I ignored requirements.txt)
15
+ - Anaconda (I used latest requirements.txt)
16
+ - Docker(Windows/Mac)
17
+ - Docker(Linux)
18
+ - Docker-Compose(Windows/Mac)
19
+ - Docker-Compose(Linux)
20
+ - Huggingface
21
+ - Others (Please Describe)
22
+ validations:
23
+ required: true
24
+
25
+ - type: dropdown
26
+ id: version
27
+ attributes:
28
+ label: Version | 版本
29
+ options:
30
+ - Please choose | 请选择
31
+ - Latest | 最新版
32
+ - Others | 非最新版
33
+ validations:
34
+ required: true
35
+
36
+ - type: dropdown
37
+ id: os
38
+ attributes:
39
+ label: OS | 操作系统
40
+ options:
41
+ - Please choose | 请选择
42
+ - Windows
43
+ - Mac
44
+ - Linux
45
+ - Docker
46
+ validations:
47
+ required: true
48
+
49
+ - type: textarea
50
+ id: describe
51
+ attributes:
52
+ label: Describe the bug | 简述
53
+ description: Describe the bug | 简述
54
+ validations:
55
+ required: true
56
+
57
+ - type: textarea
58
+ id: screenshot
59
+ attributes:
60
+ label: Screen Shot | 有帮助的截图
61
+ description: Screen Shot | 有帮助的截图
62
+ validations:
63
+ required: true
64
+
65
+ - type: textarea
66
+ id: traceback
67
+ attributes:
68
+ label: Terminal Traceback & Material to Help Reproduce Bugs | 终端traceback(如有) + 帮助我们复现的测试材料样本(如有)
69
+ description: Terminal Traceback & Material to Help Reproduce Bugs | 终端traceback(如有) + 帮助我们复现的测试材料样本(如有)
70
+
71
+
72
+
73
+
74
+
75
+
.github/ISSUE_TEMPLATE/feature_request.yml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Feature Request | 功能请求
2
+ description: "Feature Request"
3
+ title: "[Feature]: "
4
+ labels: []
5
+ body:
6
+ - type: dropdown
7
+ id: download
8
+ attributes:
9
+ label: Class | 类型
10
+ options:
11
+ - Please choose | 请选择
12
+ - 其他
13
+ - 函数插件
14
+ - 大语言模型
15
+ - 程序主体
16
+ validations:
17
+ required: false
18
+
19
+ - type: textarea
20
+ id: traceback
21
+ attributes:
22
+ label: Feature Request | 功能请求
23
+ description: Feature Request | 功能请求
24
+
25
+
26
+
27
+
28
+
.github/workflows/build-with-chatglm.yml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-github-packages
2
+ name: Create and publish a Docker image for ChatGLM support
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - 'master'
8
+
9
+ env:
10
+ REGISTRY: ghcr.io
11
+ IMAGE_NAME: ${{ github.repository }}_chatglm_moss
12
+
13
+ jobs:
14
+ build-and-push-image:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ contents: read
18
+ packages: write
19
+
20
+ steps:
21
+ - name: Checkout repository
22
+ uses: actions/checkout@v3
23
+
24
+ - name: Log in to the Container registry
25
+ uses: docker/login-action@v2
26
+ with:
27
+ registry: ${{ env.REGISTRY }}
28
+ username: ${{ github.actor }}
29
+ password: ${{ secrets.GITHUB_TOKEN }}
30
+
31
+ - name: Extract metadata (tags, labels) for Docker
32
+ id: meta
33
+ uses: docker/metadata-action@v4
34
+ with:
35
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
36
+
37
+ - name: Build and push Docker image
38
+ uses: docker/build-push-action@v4
39
+ with:
40
+ context: .
41
+ push: true
42
+ file: docs/GithubAction+ChatGLM+Moss
43
+ tags: ${{ steps.meta.outputs.tags }}
44
+ labels: ${{ steps.meta.outputs.labels }}
.github/workflows/build-with-jittorllms.yml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-github-packages
2
+ name: Create and publish a Docker image for ChatGLM support
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - 'master'
8
+
9
+ env:
10
+ REGISTRY: ghcr.io
11
+ IMAGE_NAME: ${{ github.repository }}_jittorllms
12
+
13
+ jobs:
14
+ build-and-push-image:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ contents: read
18
+ packages: write
19
+
20
+ steps:
21
+ - name: Checkout repository
22
+ uses: actions/checkout@v3
23
+
24
+ - name: Log in to the Container registry
25
+ uses: docker/login-action@v2
26
+ with:
27
+ registry: ${{ env.REGISTRY }}
28
+ username: ${{ github.actor }}
29
+ password: ${{ secrets.GITHUB_TOKEN }}
30
+
31
+ - name: Extract metadata (tags, labels) for Docker
32
+ id: meta
33
+ uses: docker/metadata-action@v4
34
+ with:
35
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
36
+
37
+ - name: Build and push Docker image
38
+ uses: docker/build-push-action@v4
39
+ with:
40
+ context: .
41
+ push: true
42
+ file: docs/GithubAction+JittorLLMs
43
+ tags: ${{ steps.meta.outputs.tags }}
44
+ labels: ${{ steps.meta.outputs.labels }}
.github/workflows/build-with-latex.yml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-github-packages
2
+ name: Create and publish a Docker image for Latex support
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - 'master'
8
+
9
+ env:
10
+ REGISTRY: ghcr.io
11
+ IMAGE_NAME: ${{ github.repository }}_with_latex
12
+
13
+ jobs:
14
+ build-and-push-image:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ contents: read
18
+ packages: write
19
+
20
+ steps:
21
+ - name: Checkout repository
22
+ uses: actions/checkout@v3
23
+
24
+ - name: Log in to the Container registry
25
+ uses: docker/login-action@v2
26
+ with:
27
+ registry: ${{ env.REGISTRY }}
28
+ username: ${{ github.actor }}
29
+ password: ${{ secrets.GITHUB_TOKEN }}
30
+
31
+ - name: Extract metadata (tags, labels) for Docker
32
+ id: meta
33
+ uses: docker/metadata-action@v4
34
+ with:
35
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
36
+
37
+ - name: Build and push Docker image
38
+ uses: docker/build-push-action@v4
39
+ with:
40
+ context: .
41
+ push: true
42
+ file: docs/GithubAction+NoLocal+Latex
43
+ tags: ${{ steps.meta.outputs.tags }}
44
+ labels: ${{ steps.meta.outputs.labels }}
.github/workflows/build-without-local-llms.yml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://docs.github.com/en/actions/publishing-packages/publishing-docker-images#publishing-images-to-github-packages
2
+ name: Create and publish a Docker image
3
+
4
+ on:
5
+ push:
6
+ branches:
7
+ - 'master'
8
+
9
+ env:
10
+ REGISTRY: ghcr.io
11
+ IMAGE_NAME: ${{ github.repository }}_nolocal
12
+
13
+ jobs:
14
+ build-and-push-image:
15
+ runs-on: ubuntu-latest
16
+ permissions:
17
+ contents: read
18
+ packages: write
19
+
20
+ steps:
21
+ - name: Checkout repository
22
+ uses: actions/checkout@v3
23
+
24
+ - name: Log in to the Container registry
25
+ uses: docker/login-action@v2
26
+ with:
27
+ registry: ${{ env.REGISTRY }}
28
+ username: ${{ github.actor }}
29
+ password: ${{ secrets.GITHUB_TOKEN }}
30
+
31
+ - name: Extract metadata (tags, labels) for Docker
32
+ id: meta
33
+ uses: docker/metadata-action@v4
34
+ with:
35
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
36
+
37
+ - name: Build and push Docker image
38
+ uses: docker/build-push-action@v4
39
+ with:
40
+ context: .
41
+ push: true
42
+ file: docs/GithubAction+NoLocal
43
+ tags: ${{ steps.meta.outputs.tags }}
44
+ labels: ${{ steps.meta.outputs.labels }}
.gitignore ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ pip-wheel-metadata/
24
+ share/python-wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+ MANIFEST
29
+
30
+ # PyInstaller
31
+ # Usually these files are written by a python script from a template
32
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
33
+ *.manifest
34
+ *.spec
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+
53
+ # Translations
54
+ *.mo
55
+ *.pot
56
+ github
57
+ .github
58
+ TEMP
59
+ TRASH
60
+
61
+ # Django stuff:
62
+ *.log
63
+ local_settings.py
64
+ db.sqlite3
65
+ db.sqlite3-journal
66
+
67
+ # Flask stuff:
68
+ instance/
69
+ .webassets-cache
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98
+ __pypackages__/
99
+
100
+ # Celery stuff
101
+ celerybeat-schedule
102
+ celerybeat.pid
103
+
104
+ # SageMath parsed files
105
+ *.sage.py
106
+
107
+ # Environments
108
+ .env
109
+ .venv
110
+ env/
111
+ venv/
112
+ ENV/
113
+ env.bak/
114
+ venv.bak/
115
+
116
+ # Spyder project settings
117
+ .spyderproject
118
+ .spyproject
119
+
120
+ # Rope project settings
121
+ .ropeproject
122
+
123
+ # mkdocs documentation
124
+ /site
125
+
126
+ # mypy
127
+ .mypy_cache/
128
+ .dmypy.json
129
+ dmypy.json
130
+
131
+ # Pyre type checker
132
+ .pyre/
133
+
134
+ .vscode
135
+ .idea
136
+
137
+ history
138
+ ssr_conf
139
+ config_private.py
140
+ gpt_log
141
+ private.md
142
+ private_upload
143
+ other_llms
144
+ cradle*
145
+ debug*
146
+ private*
147
+ crazy_functions/test_project/pdf_and_word
148
+ crazy_functions/test_samples
149
+ request_llm/jittorllms
150
+ multi-language
151
+ request_llm/moss
152
+ media
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 此Dockerfile适用于“无本地模型”的环境构建,如果需要使用chatglm等本地模型或者latex运行依赖,请参考 docker-compose.yml
2
+ # 如何构建: 先修改 `config.py`, 然后 `docker build -t gpt-academic . `
3
+ # 如何运行(Linux下): `docker run --rm -it --net=host gpt-academic `
4
+ # 如何运行(其他操作系统,选择任意一个固定端口50923): `docker run --rm -it -e WEB_PORT=50923 -p 50923:50923 gpt-academic `
5
+ FROM python:3.11
6
+
7
+
8
+ # 非必要步骤,更换pip源
9
+ RUN echo '[global]' > /etc/pip.conf && \
10
+ echo 'index-url = https://mirrors.aliyun.com/pypi/simple/' >> /etc/pip.conf && \
11
+ echo 'trusted-host = mirrors.aliyun.com' >> /etc/pip.conf
12
+
13
+
14
+ # 进入工作路径
15
+ WORKDIR /gpt
16
+
17
+
18
+ # 安装大部分依赖,利用Docker缓存加速以后的构建
19
+ COPY requirements.txt ./
20
+ COPY ./docs/gradio-3.32.2-py3-none-any.whl ./docs/gradio-3.32.2-py3-none-any.whl
21
+ RUN pip3 install -r requirements.txt
22
+
23
+
24
+ # 装载项目文件,安装剩余依赖
25
+ COPY . .
26
+ RUN pip3 install -r requirements.txt
27
+
28
+
29
+ # 非必要步骤,用于预热模块
30
+ RUN python3 -c 'from check_proxy import warm_up_modules; warm_up_modules()'
31
+
32
+
33
+ # 启动
34
+ CMD ["python3", "-u", "main.py"]
LICENSE ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ <one line to give the program's name and a brief idea of what it does.>
635
+ Copyright (C) <year> <name of author>
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ <program> Copyright (C) <year> <name of author>
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get your employer (if you work as a programmer) or school,
665
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
666
+ For more information on this, and how to apply and follow the GNU GPL, see
667
+ <https://www.gnu.org/licenses/>.
668
+
669
+ The GNU General Public License does not permit incorporating your program
670
+ into proprietary programs. If your program is a subroutine library, you
671
+ may consider it more useful to permit linking proprietary applications with
672
+ the library. If this is what you want to do, use the GNU Lesser General
673
+ Public License instead of this License. But first, please read
674
+ <https://www.gnu.org/licenses/why-not-lgpl.html>.
README.md ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: GPT-Academic
3
+ emoji: 😻
4
+ colorFrom: blue
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 3.32.0
8
+ app_file: app.py
9
+ pinned: false
10
+ duplicated_from: qingxu98/gpt-academic
11
+ ---
12
+
13
+ # ChatGPT 学术优化
14
+ > **Note**
15
+ >
16
+ > 2023.7.8: Gradio, Pydantic依赖调整,已修改 `requirements.txt`。请及时**更新代码**,安装依赖时,请严格选择`requirements.txt`中**指定的版本**
17
+ >
18
+ > `pip install -r requirements.txt`
19
+
20
+
21
+ # <div align=center><img src="docs/logo.png" width="40"> GPT 学术优化 (GPT Academic)</div>
22
+
23
+ **如果喜欢这个项目,请给它一个Star;如果您发明了好用的快捷键或函数插件,欢迎发pull requests!**
24
+
25
+ If you like this project, please give it a Star. If you've come up with more useful academic shortcuts or functional plugins, feel free to open an issue or pull request. We also have a README in [English|](docs/README_EN.md)[日本語|](docs/README_JP.md)[한국어|](https://github.com/mldljyh/ko_gpt_academic)[Русский|](docs/README_RS.md)[Français](docs/README_FR.md) translated by this project itself.
26
+ To translate this project to arbitrary language with GPT, read and run [`multi_language.py`](multi_language.py) (experimental).
27
+
28
+ > **Note**
29
+ >
30
+ > 1.请注意只有 **高亮** 标识的函数插件(按钮)才支持读取文件,部分插件位于插件区的**下拉菜单**中。另外我们以**最高优先级**欢迎和处理任何新插件的PR。
31
+ >
32
+ > 2.本项目中每个文件的功能都在[自译解报告`self_analysis.md`](https://github.com/binary-husky/gpt_academic/wiki/GPT‐Academic项目自译解报告)详细说明。随着版本的迭代,您也可以随时自行点击相关函数插件,调用GPT重新生成项目的自我解析报告。常见问题[`wiki`](https://github.com/binary-husky/gpt_academic/wiki)。[安装方法](#installation) | [配置说明](https://github.com/binary-husky/gpt_academic/wiki/%E9%A1%B9%E7%9B%AE%E9%85%8D%E7%BD%AE%E8%AF%B4%E6%98%8E)。
33
+ >
34
+ > 3.本项目兼容并鼓励尝试国产大语言模型ChatGLM和Moss等等。支持多个api-key共存,可在配置文件中填写如`API_KEY="openai-key1,openai-key2,azure-key3,api2d-key4"`。需要临时更换`API_KEY`时,在输入区输入临时的`API_KEY`然后回车键提交后即可生效。
35
+
36
+
37
+
38
+
39
+ <div align="center">
40
+
41
+ 功能(⭐= 近期新增功能) | 描述
42
+ --- | ---
43
+ ⭐[接入新模型](https://github.com/binary-husky/gpt_academic/wiki/%E5%A6%82%E4%BD%95%E5%88%87%E6%8D%A2%E6%A8%A1%E5%9E%8B)! | 百度[千帆](https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Nlks5zkzu)与文心一言, [通义千问](https://modelscope.cn/models/qwen/Qwen-7B-Chat/summary),上海AI-Lab[书生](https://github.com/InternLM/InternLM),讯飞[星火](https://xinghuo.xfyun.cn/),[LLaMa2](https://huggingface.co/meta-llama/Llama-2-7b-chat-hf)
44
+ 一键润色 | 支持一键润色、一键查找论文语法错误
45
+ 一键中英互译 | 一键中英互译
46
+ 一键代码解释 | 显示代码、解释代码、生成代码、给代码加注释
47
+ [自定义快捷键](https://www.bilibili.com/video/BV14s4y1E7jN) | 支持自定义快捷键
48
+ 模块化设计 | 支持自定义强大的[函数插件](https://github.com/binary-husky/gpt_academic/tree/master/crazy_functions),插件支持[热更新](https://github.com/binary-husky/gpt_academic/wiki/%E5%87%BD%E6%95%B0%E6%8F%92%E4%BB%B6%E6%8C%87%E5%8D%97)
49
+ [自我程序剖析](https://www.bilibili.com/video/BV1cj411A7VW) | [函数插件] [一键读懂](https://github.com/binary-husky/gpt_academic/wiki/chatgpt-academic%E9%A1%B9%E7%9B%AE%E8%87%AA%E8%AF%91%E8%A7%A3%E6%8A%A5%E5%91%8A)本项目的源代码
50
+ [程序剖析](https://www.bilibili.com/video/BV1cj411A7VW) | [函数插件] 一键可以剖析其他Python/C/C++/Java/Lua/...项目树
51
+ 读论文、[翻译](https://www.bilibili.com/video/BV1KT411x7Wn)论文 | [函数插件] 一键解读latex/pdf论文全文并生成摘要
52
+ Latex全文[翻译](https://www.bilibili.com/video/BV1nk4y1Y7Js/)、[润色](https://www.bilibili.com/video/BV1FT411H7c5/) | [函数插件] 一键翻译或润色latex论文
53
+ 批量注释生成 | [函数插件] 一键批量生成函数注释
54
+ Markdown[中英互译](https://www.bilibili.com/video/BV1yo4y157jV/) | [函数插件] 看到上面5种语言的[README](https://github.com/binary-husky/gpt_academic/blob/master/docs/README_EN.md)了吗?
55
+ chat分析报告生成 | [函数插件] 运行后自动生成总结汇报
56
+ [PDF论文全文翻译功能](https://www.bilibili.com/video/BV1KT411x7Wn) | [函数插件] PDF论文提取题目&摘要+翻译全文(多线程)
57
+ [Arxiv小助手](https://www.bilibili.com/video/BV1LM4y1279X) | [函数插件] 输入arxiv文章url即可一键翻译摘要+下载PDF
58
+ Latex论文一键校对 | [函数插件] 仿Grammarly对Latex文章进行语法、拼写纠错+输出对照PDF
59
+ [谷歌学术统合小助手](https://www.bilibili.com/video/BV19L411U7ia) | [函数插件] 给定任意谷歌学术搜索页面URL,让gpt帮你[写relatedworks](https://www.bilibili.com/video/BV1GP411U7Az/)
60
+ 互联网信息聚合+GPT | [函数���件] 一键[让GPT从互联网获取信息](https://www.bilibili.com/video/BV1om4y127ck)回答问题,让信息永不过时
61
+ ⭐Arxiv论文精细翻译 ([Docker](https://github.com/binary-husky/gpt_academic/pkgs/container/gpt_academic_with_latex)) | [函数插件] 一键[以超高质量翻译arxiv论文](https://www.bilibili.com/video/BV1dz4y1v77A/),目前最好的论文翻译工具
62
+ ⭐[实时语音对话输入](https://github.com/binary-husky/gpt_academic/blob/master/docs/use_audio.md) | [函数插件] 异步[监听音频](https://www.bilibili.com/video/BV1AV4y187Uy/),自动断句,自动寻找回答时机
63
+ 公式/图片/表格显示 | 可以同时显示公式的[tex形式和渲染形式](https://user-images.githubusercontent.com/96192199/230598842-1d7fcddd-815d-40ee-af60-baf488a199df.png),支持公式、代码高亮
64
+ 多线程函数插件支持 | 支持多线调用chatgpt,一键处理[海量文本](https://www.bilibili.com/video/BV1FT411H7c5/)或程序
65
+ 启动暗色[主题](https://github.com/binary-husky/gpt_academic/issues/173) | 在浏览器url后面添加```/?__theme=dark```可以切换dark主题
66
+ [多LLM模型](https://www.bilibili.com/video/BV1wT411p7yf)支持 | 同时被GPT3.5、GPT4、[清华ChatGLM2](https://github.com/THUDM/ChatGLM2-6B)、[复旦MOSS](https://github.com/OpenLMLab/MOSS)同时伺候的感觉一定会很不错吧?
67
+ ⭐ChatGLM2微调模型 | 支持加载ChatGLM2微调模型,提供ChatGLM2微调辅助插件
68
+ 更多LLM模型接入,支持[huggingface部署](https://huggingface.co/spaces/qingxu98/gpt-academic) | 加入Newbing接口(新必应),引入清华[Jittorllms](https://github.com/Jittor/JittorLLMs)支持[LLaMA](https://github.com/facebookresearch/llama)和[盘古α](https://openi.org.cn/pangu/)
69
+ ⭐[void-terminal](https://github.com/binary-husky/void-terminal) pip包 | 脱离GUI,在Python中直接调用本项目的所有函数插件(开发中)
70
+ ⭐虚空终端插件 | [函数插件] 用自然语言,直接调度本项目其他插件
71
+ 更多新功能展示 (图像生成等) …… | 见本文档结尾处 ……
72
+ </div>
73
+
74
+
75
+ - 新界面(修改`config.py`中的LAYOUT选项即可实现“左右布局”和“上下布局”的切换)
76
+ <div align="center">
77
+ <img src="https://user-images.githubusercontent.com/96192199/230361456-61078362-a966-4eb5-b49e-3c62ef18b860.gif" width="700" >
78
+ </div>
79
+
80
+
81
+ - 所有按钮都通过读取functional.py动态生成,可随意加自定义功能,解放粘贴板
82
+ <div align="center">
83
+ <img src="https://user-images.githubusercontent.com/96192199/231975334-b4788e91-4887-412f-8b43-2b9c5f41d248.gif" width="700" >
84
+ </div>
85
+
86
+ - 润色/纠错
87
+ <div align="center">
88
+ <img src="https://user-images.githubusercontent.com/96192199/231980294-f374bdcb-3309-4560-b424-38ef39f04ebd.gif" width="700" >
89
+ </div>
90
+
91
+ - 如果输出包含公式,会同时以tex形式和渲染形式显示,方便复制和阅读
92
+ <div align="center">
93
+ <img src="https://user-images.githubusercontent.com/96192199/230598842-1d7fcddd-815d-40ee-af60-baf488a199df.png" width="700" >
94
+ </div>
95
+
96
+ - 懒得看项目代码?整个工程直接给chatgpt炫嘴里
97
+ <div align="center">
98
+ <img src="https://user-images.githubusercontent.com/96192199/226935232-6b6a73ce-8900-4aee-93f9-733c7e6fef53.png" width="700" >
99
+ </div>
100
+
101
+ - 多种大语言模型混合调用(ChatGLM + OpenAI-GPT3.5 + [API2D](https://api2d.com/)-GPT4)
102
+ <div align="center">
103
+ <img src="https://user-images.githubusercontent.com/96192199/232537274-deca0563-7aa6-4b5d-94a2-b7c453c47794.png" width="700" >
104
+ </div>
105
+
106
+ # Installation
107
+ ### 安装方法I:直接运行 (Windows, Linux or MacOS)
108
+
109
+ 1. 下载项目
110
+ ```sh
111
+ git clone --depth=1 https://github.com/binary-husky/gpt_academic.git
112
+ cd gpt_academic
113
+ ```
114
+
115
+ 2. 配置API_KEY
116
+
117
+ 在`config.py`中,配置API KEY等设置,[点击查看特殊网络环境设置方法](https://github.com/binary-husky/gpt_academic/issues/1) 。
118
+
119
+ (P.S. 程序运行时会优先检查是否存在名为`config_private.py`的私密配置文件,并用其中的配置覆盖`config.py`的同名配置。因此,如果您能理解我们的配置读取逻辑,我们强烈建议您在`config.py`旁边创建一个名为`config_private.py`的新配置文件,并把`config.py`中的配置转移(复制)到`config_private.py`中(仅复制您修改过的配置条目即可)。`config_private.py`不受git管控,可以让您的隐私信息更加安全。P.S.项目同样支持通过`环境变量`配置大多数选项,环境变量的书写格式参考`docker-compose`文件。读取优先级: `环境变量` > `config_private.py` > `config.py`)
120
+
121
+
122
+ 3. 安装依赖
123
+ ```sh
124
+ # (选择I: 如熟悉python)(python版本3.9以上,越新越好),备注:使用官方pip源或者阿里pip源,临时换源方法:python -m pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
125
+ python -m pip install -r requirements.txt
126
+
127
+ # (选择II: 如不熟悉python)使用anaconda,步骤也是类似的 (https://www.bilibili.com/video/BV1rc411W7Dr):
128
+ conda create -n gptac_venv python=3.11 # 创建anaconda环境
129
+ conda activate gptac_venv # 激活anaconda环境
130
+ python -m pip install -r requirements.txt # 这个步骤和pip安装一样的步骤
131
+ ```
132
+
133
+
134
+ <details><summary>如果需要支持清华ChatGLM2/复旦MOSS/RWKV作为后端,请点击展开此处</summary>
135
+ <p>
136
+
137
+ 【可选步骤】如果需要支持清华ChatGLM2/复旦MOSS作为后端,需要额外安装更多依赖(前提条件:熟悉Python + 用过Pytorch + 电脑配置够强):
138
+ ```sh
139
+ # 【可选步骤I】支持清华ChatGLM2。清华ChatGLM备注:如果遇到"Call ChatGLM fail 不能正常加载ChatGLM的参数" 错误,参考如下: 1:以上默认安装的为torch+cpu版,使用cuda需要卸载torch重新安装torch+cuda; 2:如因本机配置不够无法加载模型,可以修改request_llm/bridge_chatglm.py中的模型精度, 将 AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True) 都修改为 AutoTokenizer.from_pretrained("THUDM/chatglm-6b-int4", trust_remote_code=True)
140
+ python -m pip install -r request_llm/requirements_chatglm.txt
141
+
142
+ # 【可选步骤II】支持复旦MOSS
143
+ python -m pip install -r request_llm/requirements_moss.txt
144
+ git clone --depth=1 https://github.com/OpenLMLab/MOSS.git request_llm/moss # 注意执行此行代码时,必须处于项目根路径
145
+
146
+ # 【可选步骤III】支持RWKV Runner
147
+ 参考wiki:https://github.com/binary-husky/gpt_academic/wiki/%E9%80%82%E9%85%8DRWKV-Runner
148
+
149
+ # 【可选步骤IV】确保config.py配置文件的AVAIL_LLM_MODELS包含了期望的模型,目前支持的全部模型如下(jittorllms系列目前仅支持docker方案):
150
+ AVAIL_LLM_MODELS = ["gpt-3.5-turbo", "api2d-gpt-3.5-turbo", "gpt-4", "api2d-gpt-4", "chatglm", "newbing", "moss"] # + ["jittorllms_rwkv", "jittorllms_pangualpha", "jittorllms_llama"]
151
+ ```
152
+
153
+ </p>
154
+ </details>
155
+
156
+
157
+
158
+ 4. 运行
159
+ ```sh
160
+ python main.py
161
+ ```
162
+
163
+ ### 安装方法II:使用Docker
164
+
165
+ [![fullcapacity](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-all-capacity.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-audio-assistant.yml)
166
+
167
+ 1. 仅ChatGPT(推荐大多数人选择,等价于docker-compose方案1)
168
+ [![basic](https://github.com/binary-husky/gpt_academic/actions/workflows/build-without-local-llms.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-without-local-llms.yml)
169
+ [![basiclatex](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-latex.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-latex.yml)
170
+ [![basicaudio](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-audio-assistant.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-audio-assistant.yml)
171
+
172
+
173
+ ``` sh
174
+ git clone --depth=1 https://github.com/binary-husky/gpt_academic.git # 下载项目
175
+ cd gpt_academic # 进入路径
176
+ nano config.py # 用任意文本编辑器编辑config.py, 配置 “Proxy”, “API_KEY” 以及 “WEB_PORT” (例如50923) 等
177
+ docker build -t gpt-academic . # 安装
178
+
179
+ #(最后一步-Linux操作系统)用`--net=host`更方便快捷
180
+ docker run --rm -it --net=host gpt-academic
181
+ #(最后一步-MacOS/Windows操作系统)只能用-p选项将容器上的端口(例如50923)暴露给主机上的端口
182
+ docker run --rm -it -e WEB_PORT=50923 -p 50923:50923 gpt-academic
183
+ ```
184
+ P.S. 如果需要依赖Latex的插件功能,请见Wiki。另外,您也可以直接使用docker-compose获取Latex功能(修改docker-compose.yml,保留方案4并删除其他方案)。
185
+
186
+ 2. ChatGPT + ChatGLM2 + MOSS + LLAMA2 + 通义千问(需要熟悉[Nvidia Docker](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#installing-on-ubuntu-and-debian)运行时)
187
+ [![chatglm](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-chatglm.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-chatglm.yml)
188
+
189
+ ``` sh
190
+ # 修改docker-compose.yml,保留方案2并删除其他方案。修改docker-compose.yml中方案2的配置,参考其中注释即可
191
+ docker-compose up
192
+ ```
193
+
194
+ 3. ChatGPT + LLAMA + 盘古 + RWKV(需要熟悉[Nvidia Docker](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#installing-on-ubuntu-and-debian)运行时)
195
+ [![jittorllms](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-jittorllms.yml/badge.svg?branch=master)](https://github.com/binary-husky/gpt_academic/actions/workflows/build-with-jittorllms.yml)
196
+
197
+ ``` sh
198
+ # 修改docker-compose.yml,保留方案3并删除其他方案。修改docker-compose.yml中方案3的配置,参考其中注释即可
199
+ docker-compose up
200
+ ```
201
+
202
+
203
+ ### 安装方法III:其他部署姿势
204
+ 1. 一键运行脚本。
205
+ 完全不熟悉python环境的Windows���户可以下载[Release](https://github.com/binary-husky/gpt_academic/releases)中发布的一键运行脚本安装无本地模型的版本。
206
+ 脚本的贡献来源是[oobabooga](https://github.com/oobabooga/one-click-installers)。
207
+
208
+ 2. 使用docker-compose运行。
209
+ 请阅读docker-compose.yml后,按照其中的提示操作即可
210
+
211
+ 3. 如何使用反代URL
212
+ 按照`config.py`中的说明配置API_URL_REDIRECT即可。
213
+
214
+ 4. 微软云AzureAPI
215
+ 按照`config.py`中的说明配置即可(AZURE_ENDPOINT等四个配置)
216
+
217
+ 5. 远程云服务器部署(需要云服务器知识与经验)。
218
+ 请访问[部署wiki-1](https://github.com/binary-husky/gpt_academic/wiki/%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E8%BF%9C%E7%A8%8B%E9%83%A8%E7%BD%B2%E6%8C%87%E5%8D%97)
219
+
220
+ 6. 使用Sealos[一键部署](https://github.com/binary-husky/gpt_academic/issues/993)。
221
+
222
+ 7. 使用WSL2(Windows Subsystem for Linux 子系统)。
223
+ 请访问[部署wiki-2](https://github.com/binary-husky/gpt_academic/wiki/%E4%BD%BF%E7%94%A8WSL2%EF%BC%88Windows-Subsystem-for-Linux-%E5%AD%90%E7%B3%BB%E7%BB%9F%EF%BC%89%E9%83%A8%E7%BD%B2)
224
+
225
+ 8. 如何在二级网址(如`http://localhost/subpath`)下运行。
226
+ 请访问[FastAPI运行说明](docs/WithFastapi.md)
227
+
228
+
229
+ # Advanced Usage
230
+ ### I:自定义新的便捷按钮(学术快捷键)
231
+ 任意文本编辑器打开`core_functional.py`,添加条目如下,然后重启程序即可。(如果按钮已经添加成功并可见,那么前缀、后缀都支持热修改,无需重启程序即可生效。)
232
+ 例如
233
+ ```
234
+ "超级英译中": {
235
+ # 前缀,会被加在你的输入之前。例如,用来描述你的要求,例如翻译、解释代码、润色等等
236
+ "Prefix": "请翻译把下面一段内容成中文,然后用一个markdown表格逐一解释文中出现的专有名词:\n\n",
237
+
238
+ # 后缀,会被加在你的输入之后。例如,配合前缀可以把你的输入内容用引号圈起来。
239
+ "Suffix": "",
240
+ },
241
+ ```
242
+ <div align="center">
243
+ <img src="https://user-images.githubusercontent.com/96192199/226899272-477c2134-ed71-4326-810c-29891fe4a508.png" width="500" >
244
+ </div>
245
+
246
+ ### II:自定义函数插件
247
+
248
+ 编写强大的函数插件来执行任何你想得到的和想不到的任务。
249
+ 本项目的插件编写、调试难度很低,只要您具备一定的python基础知识,就可以仿照我们提供的模板实现自己的插件功能。
250
+ 详情请参考[函数插件指南](https://github.com/binary-husky/gpt_academic/wiki/%E5%87%BD%E6%95%B0%E6%8F%92%E4%BB%B6%E6%8C%87%E5%8D%97)。
251
+
252
+
253
+ # Latest Update
254
+ ### I:新功能动态
255
+
256
+ 1. 对话保存功能。在函数插件区调用 `保存当前的对话` 即可将当前对话保存为可读+可复原的html文件,
257
+ 另外在函数插件区(下拉菜单)调用 `载入对话历史存档` ,即可还原之前的会话。
258
+ Tip:不指定文件直接点击 `载入对话历史存档` 可以查看历史html存档缓存。
259
+ <div align="center">
260
+ <img src="https://user-images.githubusercontent.com/96192199/235222390-24a9acc0-680f-49f5-bc81-2f3161f1e049.png" width="500" >
261
+ </div>
262
+
263
+ 2. ⭐Latex/Arxiv论文翻译功能⭐
264
+ <div align="center">
265
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/002a1a75-ace0-4e6a-94e2-ec1406a746f1" height="250" > ===>
266
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/9fdcc391-f823-464f-9322-f8719677043b" height="250" >
267
+ </div>
268
+
269
+ 3. 虚空终端(从自然语言输入中,理解用户意图+自动调用其他插件)
270
+
271
+ - 步骤一:输入 “ 请调用插件翻译PDF论文,地址为https://storage.googleapis.com/deepmind-media/alphago/AlphaGoNaturePaper.pdf ”
272
+ - 步骤二:点击“虚空终端”
273
+
274
+ <div align="center">
275
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/66f1b044-e9ff-4eed-9126-5d4f3668f1ed" width="500" >
276
+ </div>
277
+
278
+ 4. 模块化功能设计,简单的接口却能支持强大的功能
279
+ <div align="center">
280
+ <img src="https://user-images.githubusercontent.com/96192199/229288270-093643c1-0018-487a-81e6-1d7809b6e90f.png" height="400" >
281
+ <img src="https://user-images.githubusercontent.com/96192199/227504931-19955f78-45cd-4d1c-adac-e71e50957915.png" height="400" >
282
+ </div>
283
+
284
+ 5. 译解其他开源项目
285
+ <div align="center">
286
+ <img src="https://user-images.githubusercontent.com/96192199/226935232-6b6a73ce-8900-4aee-93f9-733c7e6fef53.png" height="250" >
287
+ <img src="https://user-images.githubusercontent.com/96192199/226969067-968a27c1-1b9c-486b-8b81-ab2de8d3f88a.png" height="250" >
288
+ </div>
289
+
290
+ 6. 装饰[live2d](https://github.com/fghrsh/live2d_demo)的小功能(默认关闭,需要修改`config.py`)
291
+ <div align="center">
292
+ <img src="https://user-images.githubusercontent.com/96192199/236432361-67739153-73e8-43fe-8111-b61296edabd9.png" width="500" >
293
+ </div>
294
+
295
+ 7. 新增MOSS大语言模型支持
296
+ <div align="center">
297
+ <img src="https://user-images.githubusercontent.com/96192199/236639178-92836f37-13af-4fdd-984d-b4450fe30336.png" width="500" >
298
+ </div>
299
+
300
+ 8. OpenAI图像生成
301
+ <div align="center">
302
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/bc7ab234-ad90-48a0-8d62-f703d9e74665" width="500" >
303
+ </div>
304
+
305
+ 9. OpenAI音频解析与总结
306
+ <div align="center">
307
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/709ccf95-3aee-498a-934a-e1c22d3d5d5b" width="500" >
308
+ </div>
309
+
310
+ 10. Latex全文校对纠错
311
+ <div align="center">
312
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/651ccd98-02c9-4464-91e1-77a6b7d1b033" height="200" > ===>
313
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/476f66d9-7716-4537-b5c1-735372c25adb" height="200">
314
+ </div>
315
+
316
+ 11. 语言、主题切换
317
+ <div align="center">
318
+ <img src="https://github.com/binary-husky/gpt_academic/assets/96192199/b6799499-b6fb-4f0c-9c8e-1b441872f4e8" width="500" >
319
+ </div>
320
+
321
+
322
+
323
+ ### II:版本:
324
+ - version 3.60(todo): 优化虚空终端,引入code interpreter和更多插件
325
+ - version 3.50: 使用自然语言调用本项目的所有函数插件(虚空终端),支持插件分类,改进UI,设计新主题
326
+ - version 3.49: 支持百度千帆平台和文心一言
327
+ - version 3.48: 支持阿里达摩院通义千问,上海AI-Lab书生,讯飞星火
328
+ - version 3.46: 支持完全脱手操作的实时语音对话
329
+ - version 3.45: 支持自定义ChatGLM2微调模型
330
+ - version 3.44: 正式支持Azure,优化界面易用性
331
+ - version 3.4: +arxiv论文翻译、latex论文批改功能
332
+ - version 3.3: +互联网信息综合功能
333
+ - version 3.2: 函数插件支持更多参数接口 (保存对话功能, 解读任意语言代码+同时询问任意的LLM组合)
334
+ - version 3.1: 支持同时问询多个gpt模型!支持api2d,支持多个apikey负载均衡
335
+ - version 3.0: 对chatglm和其他小型llm的支持
336
+ - version 2.6: 重构了插件结构,提高了交互性,加入更多插件
337
+ - version 2.5: 自更新,解决总结大工程源代码时文本过长、token溢出的问题
338
+ - version 2.4: (1)新增PDF全文翻译功能; (2)新增输入区切换位置的功能; (3)新增垂直布局选项; (4)多线程函数插件优化。
339
+ - version 2.3: 增强多线程交互性
340
+ - version 2.2: 函数插件支持热重载
341
+ - version 2.1: 可折叠式布局
342
+ - version 2.0: 引入模块化函数插件
343
+ - version 1.0: 基础功能
344
+
345
+ gpt_academic开发者QQ群-2:610599535
346
+
347
+ - 已知问题
348
+ - 某些浏览器翻译插件干扰此软件前端的运行
349
+ - 官方Gradio目前有很多兼容性Bug,请务必使用`requirement.txt`安装Gradio
350
+
351
+ ### III:主题
352
+ 可以通过修改`THEME`选项(config.py)变更主题
353
+ 1. `Chuanhu-Small-and-Beautiful` [网址](https://github.com/GaiZhenbiao/ChuanhuChatGPT/)
354
+
355
+
356
+ ### IV:参考与学习
357
+
358
+ ```
359
+ 代码中参考了很多其他优秀项目中的设计,顺序不分先后:
360
+
361
+ # 清华ChatGLM2-6B:
362
+ https://github.com/THUDM/ChatGLM2-6B
363
+
364
+ # 清华JittorLLMs:
365
+ https://github.com/Jittor/JittorLLMs
366
+
367
+ # ChatPaper:
368
+ https://github.com/kaixindelele/ChatPaper
369
+
370
+ # Edge-GPT:
371
+ https://github.com/acheong08/EdgeGPT
372
+
373
+ # ChuanhuChatGPT:
374
+ https://github.com/GaiZhenbiao/ChuanhuChatGPT
375
+
376
+ # Oobabooga one-click installer:
377
+ https://github.com/oobabooga/one-click-installers
378
+
379
+ # More:
380
+ https://github.com/gradio-app/gradio
381
+ https://github.com/fghrsh/live2d_demo
382
+ ```
app.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os; os.environ['no_proxy'] = '*' # 避免代理网络产生意外污染
2
+
3
+ def main():
4
+ import subprocess, sys
5
+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'gradio-stable-fork'])
6
+ import gradio as gr
7
+ from request_llm.bridge_all import predict
8
+ from toolbox import format_io, find_free_port, on_file_uploaded, on_report_generated, get_conf, ArgsGeneralWrapper, load_chat_cookies, DummyWith
9
+ # 建议您复制一个config_private.py放自己的秘密, 如API和代理网址, 避免不小心传github被别人看到
10
+ proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION = get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION')
11
+ CHATBOT_HEIGHT, LAYOUT, AVAIL_LLM_MODELS, AUTO_CLEAR_TXT = get_conf('CHATBOT_HEIGHT', 'LAYOUT', 'AVAIL_LLM_MODELS', 'AUTO_CLEAR_TXT')
12
+ ENABLE_AUDIO, AUTO_CLEAR_TXT = get_conf('ENABLE_AUDIO', 'AUTO_CLEAR_TXT')
13
+
14
+ # 如果WEB_PORT是-1, 则随机选取WEB端口
15
+ PORT = find_free_port() if WEB_PORT <= 0 else WEB_PORT
16
+ from check_proxy import get_current_version
17
+ from themes.theme import adjust_theme, advanced_css, theme_declaration
18
+ initial_prompt = "Serve me as a writing and programming assistant."
19
+ title_html = f"<h1 align=\"center\">GPT 学术优化 {get_current_version()}</h1>{theme_declaration}"
20
+ description = "代码开源和更新[地址🚀](https://github.com/binary-husky/gpt_academic),"
21
+ description += "感谢热情的[开发者们❤️](https://github.com/binary-husky/gpt_academic/graphs/contributors)"
22
+
23
+ # 问询记录, python 版本建议3.9+(越新越好)
24
+ import logging, uuid
25
+ os.makedirs("gpt_log", exist_ok=True)
26
+ try:logging.basicConfig(filename="gpt_log/chat_secrets.log", level=logging.INFO, encoding="utf-8", format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
27
+ except:logging.basicConfig(filename="gpt_log/chat_secrets.log", level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
28
+ # Disable logging output from the 'httpx' logger
29
+ logging.getLogger("httpx").setLevel(logging.WARNING)
30
+ print("所有问询记录将自动保存在本地目录./gpt_log/chat_secrets.log, 请注意自我隐私保护哦!")
31
+
32
+ # 一些普通功能模块
33
+ from core_functional import get_core_functions
34
+ functional = get_core_functions()
35
+
36
+ # 高级函数插件
37
+ from crazy_functional import get_crazy_functions
38
+ DEFAULT_FN_GROUPS, = get_conf('DEFAULT_FN_GROUPS')
39
+ plugins = get_crazy_functions()
40
+ all_plugin_groups = list(set([g for _, plugin in plugins.items() for g in plugin['Group'].split('|')]))
41
+ match_group = lambda tags, groups: any([g in groups for g in tags.split('|')])
42
+
43
+ # 处理markdown文本格式的转变
44
+ gr.Chatbot.postprocess = format_io
45
+
46
+ # 做一些外观色彩上的调整
47
+ set_theme = adjust_theme()
48
+
49
+ # 代理与自动更新
50
+ from check_proxy import check_proxy, auto_update, warm_up_modules
51
+ proxy_info = check_proxy(proxies)
52
+
53
+ gr_L1 = lambda: gr.Row().style()
54
+ gr_L2 = lambda scale, elem_id: gr.Column(scale=scale, elem_id=elem_id)
55
+ if LAYOUT == "TOP-DOWN":
56
+ gr_L1 = lambda: DummyWith()
57
+ gr_L2 = lambda scale, elem_id: gr.Row()
58
+ CHATBOT_HEIGHT /= 2
59
+
60
+ cancel_handles = []
61
+ with gr.Blocks(title="GPT 学术优化", theme=set_theme, analytics_enabled=False, css=advanced_css) as demo:
62
+ gr.HTML(title_html)
63
+ gr.HTML('''<center><a href="https://huggingface.co/spaces/qingxu98/gpt-academic?duplicate=true"><img src="https://bit.ly/3gLdBN6" alt="Duplicate Space"></a>请您打开此页面后务必点击上方的“复制空间”(Duplicate Space)按钮!<font color="#FF00FF">使用时,先在输入框填入API-KEY然后回车。</font><br/>切忌在“复制空间”(Duplicate Space)之前填入API_KEY或进行提问,否则您的API_KEY将极可能被空间所有者攫取!<br/>支持任意数量的OpenAI的密钥和API2D的密钥共存,例如输入"OpenAI密钥1,API2D密钥2",然后提交,即可同时使用两种模型接口。</center>''')
64
+ cookies = gr.State(load_chat_cookies())
65
+ with gr_L1():
66
+ with gr_L2(scale=2, elem_id="gpt-chat"):
67
+ chatbot = gr.Chatbot(label=f"当前模型:{LLM_MODEL}", elem_id="gpt-chatbot")
68
+ if LAYOUT == "TOP-DOWN": chatbot.style(height=CHATBOT_HEIGHT)
69
+ history = gr.State([])
70
+ with gr_L2(scale=1, elem_id="gpt-panel"):
71
+ with gr.Accordion("输入区", open=True, elem_id="input-panel") as area_input_primary:
72
+ with gr.Row():
73
+ txt = gr.Textbox(show_label=False, lines=2, placeholder="输入问题或API密钥,输入多个密钥时,用英文逗号间隔。支持OpenAI密钥和API2D密钥共存。").style(container=False)
74
+ with gr.Row():
75
+ submitBtn = gr.Button("提交", variant="primary")
76
+ with gr.Row():
77
+ resetBtn = gr.Button("重置", variant="secondary"); resetBtn.style(size="sm")
78
+ stopBtn = gr.Button("停止", variant="secondary"); stopBtn.style(size="sm")
79
+ clearBtn = gr.Button("清除", variant="secondary", visible=False); clearBtn.style(size="sm")
80
+ if ENABLE_AUDIO:
81
+ with gr.Row():
82
+ audio_mic = gr.Audio(source="microphone", type="numpy", streaming=True, show_label=False).style(container=False)
83
+ with gr.Row():
84
+ status = gr.Markdown(f"Tip: 按Enter提交, 按Shift+Enter换行。当前模型: {LLM_MODEL} \n {proxy_info}", elem_id="state-panel")
85
+ with gr.Accordion("基础功能区", open=True, elem_id="basic-panel") as area_basic_fn:
86
+ with gr.Row():
87
+ for k in functional:
88
+ if ("Visible" in functional[k]) and (not functional[k]["Visible"]): continue
89
+ variant = functional[k]["Color"] if "Color" in functional[k] else "secondary"
90
+ functional[k]["Button"] = gr.Button(k, variant=variant)
91
+ functional[k]["Button"].style(size="sm")
92
+ with gr.Accordion("函数插件区", open=True, elem_id="plugin-panel") as area_crazy_fn:
93
+ with gr.Row():
94
+ gr.Markdown("插件可读取“输入区”文本/路径作为参数(上传文件自动修正路径)")
95
+ with gr.Row(elem_id="input-plugin-group"):
96
+ plugin_group_sel = gr.Dropdown(choices=all_plugin_groups, label='', show_label=False, value=DEFAULT_FN_GROUPS,
97
+ multiselect=True, interactive=True, elem_classes='normal_mut_select').style(container=False)
98
+ with gr.Row():
99
+ for k, plugin in plugins.items():
100
+ if not plugin.get("AsButton", True): continue
101
+ visible = True if match_group(plugin['Group'], DEFAULT_FN_GROUPS) else False
102
+ variant = plugins[k]["Color"] if "Color" in plugin else "secondary"
103
+ plugin['Button'] = plugins[k]['Button'] = gr.Button(k, variant=variant, visible=visible).style(size="sm")
104
+ with gr.Row():
105
+ with gr.Accordion("更多函数插件", open=True):
106
+ dropdown_fn_list = []
107
+ for k, plugin in plugins.items():
108
+ if not match_group(plugin['Group'], DEFAULT_FN_GROUPS): continue
109
+ if not plugin.get("AsButton", True): dropdown_fn_list.append(k) # 排除已经是按钮的插件
110
+ elif plugin.get('AdvancedArgs', False): dropdown_fn_list.append(k) # 对于需要高级参数的插件,亦在下拉菜单中显示
111
+ with gr.Row():
112
+ dropdown = gr.Dropdown(dropdown_fn_list, value=r"打开插件列表", label="", show_label=False).style(container=False)
113
+ with gr.Row():
114
+ plugin_advanced_arg = gr.Textbox(show_label=True, label="高级参数输入区", visible=False,
115
+ placeholder="这里是特殊函数插件的高级参数输入区").style(container=False)
116
+ with gr.Row():
117
+ switchy_bt = gr.Button(r"请先从插件列表中选择", variant="secondary").style(size="sm")
118
+ with gr.Row():
119
+ with gr.Accordion("点击展开“文件上传区”。上传本地文件/压缩包供函数插件调用。", open=False) as area_file_up:
120
+ file_upload = gr.Files(label="任何文件, 但推荐上传压缩文件(zip, tar)", file_count="multiple")
121
+ with gr.Accordion("更换模型 & SysPrompt & 交互界面布局", open=(LAYOUT == "TOP-DOWN"), elem_id="interact-panel"):
122
+ system_prompt = gr.Textbox(show_label=True, placeholder=f"System Prompt", label="System prompt", value=initial_prompt)
123
+ top_p = gr.Slider(minimum=-0, maximum=1.0, value=1.0, step=0.01,interactive=True, label="Top-p (nucleus sampling)",)
124
+ temperature = gr.Slider(minimum=-0, maximum=2.0, value=1.0, step=0.01, interactive=True, label="Temperature",)
125
+ max_length_sl = gr.Slider(minimum=256, maximum=8192, value=4096, step=1, interactive=True, label="Local LLM MaxLength",)
126
+ checkboxes = gr.CheckboxGroup(["基础功能区", "函数插件区", "底部输入区", "输入清除键", "插件参数区"], value=["基础功能区", "函数插件区"], label="显示/隐藏功能区")
127
+ md_dropdown = gr.Dropdown(AVAIL_LLM_MODELS, value=LLM_MODEL, label="更换LLM模型/请求源").style(container=False)
128
+ gr.Markdown(description)
129
+ with gr.Accordion("备选输入区", open=True, visible=False, elem_id="input-panel2") as area_input_secondary:
130
+ with gr.Row():
131
+ txt2 = gr.Textbox(show_label=False, placeholder="Input question here.", label="输入区2").style(container=False)
132
+ with gr.Row():
133
+ submitBtn2 = gr.Button("提交", variant="primary")
134
+ with gr.Row():
135
+ resetBtn2 = gr.Button("重置", variant="secondary"); resetBtn2.style(size="sm")
136
+ stopBtn2 = gr.Button("停止", variant="secondary"); stopBtn2.style(size="sm")
137
+ clearBtn2 = gr.Button("清除", variant="secondary", visible=False); clearBtn2.style(size="sm")
138
+
139
+ # 功能区显示开关与功能区的互动
140
+ def fn_area_visibility(a):
141
+ ret = {}
142
+ ret.update({area_basic_fn: gr.update(visible=("基础功能区" in a))})
143
+ ret.update({area_crazy_fn: gr.update(visible=("函数插件区" in a))})
144
+ ret.update({area_input_primary: gr.update(visible=("底部输入区" not in a))})
145
+ ret.update({area_input_secondary: gr.update(visible=("底部输入区" in a))})
146
+ ret.update({clearBtn: gr.update(visible=("输入清除键" in a))})
147
+ ret.update({clearBtn2: gr.update(visible=("输入清除键" in a))})
148
+ ret.update({plugin_advanced_arg: gr.update(visible=("插件参数区" in a))})
149
+ if "底部输入区" in a: ret.update({txt: gr.update(value="")})
150
+ return ret
151
+ checkboxes.select(fn_area_visibility, [checkboxes], [area_basic_fn, area_crazy_fn, area_input_primary, area_input_secondary, txt, txt2, clearBtn, clearBtn2, plugin_advanced_arg] )
152
+ # 整理反复出现的控件句柄组合
153
+ input_combo = [cookies, max_length_sl, md_dropdown, txt, txt2, top_p, temperature, chatbot, history, system_prompt, plugin_advanced_arg]
154
+ output_combo = [cookies, chatbot, history, status]
155
+ predict_args = dict(fn=ArgsGeneralWrapper(predict), inputs=input_combo, outputs=output_combo)
156
+ # 提交按钮、重置按钮
157
+ cancel_handles.append(txt.submit(**predict_args))
158
+ cancel_handles.append(txt2.submit(**predict_args))
159
+ cancel_handles.append(submitBtn.click(**predict_args))
160
+ cancel_handles.append(submitBtn2.click(**predict_args))
161
+ resetBtn.click(lambda: ([], [], "已重置"), None, [chatbot, history, status])
162
+ resetBtn2.click(lambda: ([], [], "已重置"), None, [chatbot, history, status])
163
+ clearBtn.click(lambda: ("",""), None, [txt, txt2])
164
+ clearBtn2.click(lambda: ("",""), None, [txt, txt2])
165
+ if AUTO_CLEAR_TXT:
166
+ submitBtn.click(lambda: ("",""), None, [txt, txt2])
167
+ submitBtn2.click(lambda: ("",""), None, [txt, txt2])
168
+ txt.submit(lambda: ("",""), None, [txt, txt2])
169
+ txt2.submit(lambda: ("",""), None, [txt, txt2])
170
+ # 基础功能区的回调函数注册
171
+ for k in functional:
172
+ if ("Visible" in functional[k]) and (not functional[k]["Visible"]): continue
173
+ click_handle = functional[k]["Button"].click(fn=ArgsGeneralWrapper(predict), inputs=[*input_combo, gr.State(True), gr.State(k)], outputs=output_combo)
174
+ cancel_handles.append(click_handle)
175
+ # 文件上传区,接收文件后与chatbot的互动
176
+ file_upload.upload(on_file_uploaded, [file_upload, chatbot, txt, txt2, checkboxes, cookies], [chatbot, txt, txt2, cookies])
177
+ # 函数插件-固定按钮区
178
+ for k in plugins:
179
+ if not plugins[k].get("AsButton", True): continue
180
+ click_handle = plugins[k]["Button"].click(ArgsGeneralWrapper(plugins[k]["Function"]), [*input_combo, gr.State(PORT)], output_combo)
181
+ click_handle.then(on_report_generated, [cookies, file_upload, chatbot], [cookies, file_upload, chatbot])
182
+ cancel_handles.append(click_handle)
183
+ # 函数插件-下拉菜单与随变按钮的互动
184
+ def on_dropdown_changed(k):
185
+ variant = plugins[k]["Color"] if "Color" in plugins[k] else "secondary"
186
+ ret = {switchy_bt: gr.update(value=k, variant=variant)}
187
+ if plugins[k].get("AdvancedArgs", False): # 是否唤起高级插件参数区
188
+ ret.update({plugin_advanced_arg: gr.update(visible=True, label=f"插件[{k}]的高级参数说明:" + plugins[k].get("ArgsReminder", [f"没有提供高级参数功能说明"]))})
189
+ else:
190
+ ret.update({plugin_advanced_arg: gr.update(visible=False, label=f"插件[{k}]不需要高级参数。")})
191
+ return ret
192
+ dropdown.select(on_dropdown_changed, [dropdown], [switchy_bt, plugin_advanced_arg] )
193
+ def on_md_dropdown_changed(k):
194
+ return {chatbot: gr.update(label="当前模型:"+k)}
195
+ md_dropdown.select(on_md_dropdown_changed, [md_dropdown], [chatbot] )
196
+ # 随变按钮的回调函数注册
197
+ def route(request: gr.Request, k, *args, **kwargs):
198
+ if k in [r"打开插件列表", r"请先从插件列表中选择"]: return
199
+ yield from ArgsGeneralWrapper(plugins[k]["Function"])(request, *args, **kwargs)
200
+ click_handle = switchy_bt.click(route,[switchy_bt, *input_combo, gr.State(PORT)], output_combo)
201
+ click_handle.then(on_report_generated, [cookies, file_upload, chatbot], [cookies, file_upload, chatbot])
202
+ cancel_handles.append(click_handle)
203
+ # 终止按钮的回调函数注册
204
+ stopBtn.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles)
205
+ stopBtn2.click(fn=None, inputs=None, outputs=None, cancels=cancel_handles)
206
+ plugins_as_btn = {name:plugin for name, plugin in plugins.items() if plugin.get('Button', None)}
207
+ def on_group_change(group_list):
208
+ btn_list = []
209
+ fns_list = []
210
+ if not group_list: # 处理特殊情况:没有选择任何插件组
211
+ return [*[plugin['Button'].update(visible=False) for _, plugin in plugins_as_btn.items()], gr.Dropdown.update(choices=[])]
212
+ for k, plugin in plugins.items():
213
+ if plugin.get("AsButton", True):
214
+ btn_list.append(plugin['Button'].update(visible=match_group(plugin['Group'], group_list))) # 刷新按钮
215
+ if plugin.get('AdvancedArgs', False): dropdown_fn_list.append(k) # 对于需要高级参数的插件,亦在下拉菜单中显示
216
+ elif match_group(plugin['Group'], group_list): fns_list.append(k) # 刷新下拉列表
217
+ return [*btn_list, gr.Dropdown.update(choices=fns_list)]
218
+ plugin_group_sel.select(fn=on_group_change, inputs=[plugin_group_sel], outputs=[*[plugin['Button'] for name, plugin in plugins_as_btn.items()], dropdown])
219
+ if ENABLE_AUDIO:
220
+ from crazy_functions.live_audio.audio_io import RealtimeAudioDistribution
221
+ rad = RealtimeAudioDistribution()
222
+ def deal_audio(audio, cookies):
223
+ rad.feed(cookies['uuid'].hex, audio)
224
+ audio_mic.stream(deal_audio, inputs=[audio_mic, cookies])
225
+
226
+ def init_cookie(cookies, chatbot):
227
+ # 为每一位访问的用户赋予一个独一无二的uuid编码
228
+ cookies.update({'uuid': uuid.uuid4()})
229
+ return cookies
230
+ demo.load(init_cookie, inputs=[cookies, chatbot], outputs=[cookies])
231
+ demo.load(lambda: 0, inputs=None, outputs=None, _js='()=>{ChatBotHeight();}')
232
+
233
+ # gradio的inbrowser触发不太稳定,回滚代码到原始的浏览器打开函数
234
+ def auto_opentab_delay():
235
+ import threading, webbrowser, time
236
+ print(f"如果浏览器没有自动打开,请复制并转到以下URL:")
237
+ print(f"\t(亮色主题): http://localhost:{PORT}")
238
+ print(f"\t(暗色主题): http://localhost:{PORT}/?__theme=dark")
239
+ def open():
240
+ time.sleep(2) # 打开浏览器
241
+ DARK_MODE, = get_conf('DARK_MODE')
242
+ if DARK_MODE: webbrowser.open_new_tab(f"http://localhost:{PORT}/?__theme=dark")
243
+ else: webbrowser.open_new_tab(f"http://localhost:{PORT}")
244
+ threading.Thread(target=open, name="open-browser", daemon=True).start()
245
+ threading.Thread(target=auto_update, name="self-upgrade", daemon=True).start()
246
+ threading.Thread(target=warm_up_modules, name="warm-up", daemon=True).start()
247
+
248
+ auto_opentab_delay()
249
+ demo.queue(concurrency_count=CONCURRENT_COUNT).launch(server_name="0.0.0.0", share=False, favicon_path="docs/logo.png", blocked_paths=["config.py","config_private.py","docker-compose.yml","Dockerfile"])
250
+
251
+ # 如果需要在二级路径下运行
252
+ # CUSTOM_PATH, = get_conf('CUSTOM_PATH')
253
+ # if CUSTOM_PATH != "/":
254
+ # from toolbox import run_gradio_in_subpath
255
+ # run_gradio_in_subpath(demo, auth=AUTHENTICATION, port=PORT, custom_path=CUSTOM_PATH)
256
+ # else:
257
+ # demo.launch(server_name="0.0.0.0", server_port=PORT, auth=AUTHENTICATION, favicon_path="docs/logo.png",
258
+ # blocked_paths=["config.py","config_private.py","docker-compose.yml","Dockerfile"])
259
+
260
+ if __name__ == "__main__":
261
+ main()
check_proxy.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ def check_proxy(proxies):
3
+ import requests
4
+ proxies_https = proxies['https'] if proxies is not None else '无'
5
+ try:
6
+ response = requests.get("https://ipapi.co/json/", proxies=proxies, timeout=4)
7
+ data = response.json()
8
+ # print(f'查询代理的地理位置,返回的结果是{data}')
9
+ if 'country_name' in data:
10
+ country = data['country_name']
11
+ result = f"代理配置 {proxies_https}, 代理所在地:{country}"
12
+ elif 'error' in data:
13
+ alternative = _check_with_backup_source(proxies)
14
+ if alternative is None:
15
+ result = f"代理配置 {proxies_https}, 代理所在地:未知,IP查询频率受限"
16
+ else:
17
+ result = f"代理配置 {proxies_https}, 代理所在地:{alternative}"
18
+ else:
19
+ result = f"代理配置 {proxies_https}, 代理数据解析失败:{data}"
20
+ print(result)
21
+ return result
22
+ except:
23
+ result = f"代理配置 {proxies_https}, 代理所在地查询超时,代理可能无效"
24
+ print(result)
25
+ return result
26
+
27
+ def _check_with_backup_source(proxies):
28
+ import random, string, requests
29
+ random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
30
+ try: return requests.get(f"http://{random_string}.edns.ip-api.com/json", proxies=proxies, timeout=4).json()['dns']['geo']
31
+ except: return None
32
+
33
+ def backup_and_download(current_version, remote_version):
34
+ """
35
+ 一键更新协议:备份和下载
36
+ """
37
+ from toolbox import get_conf
38
+ import shutil
39
+ import os
40
+ import requests
41
+ import zipfile
42
+ os.makedirs(f'./history', exist_ok=True)
43
+ backup_dir = f'./history/backup-{current_version}/'
44
+ new_version_dir = f'./history/new-version-{remote_version}/'
45
+ if os.path.exists(new_version_dir):
46
+ return new_version_dir
47
+ os.makedirs(new_version_dir)
48
+ shutil.copytree('./', backup_dir, ignore=lambda x, y: ['history'])
49
+ proxies, = get_conf('proxies')
50
+ r = requests.get(
51
+ 'https://github.com/binary-husky/chatgpt_academic/archive/refs/heads/master.zip', proxies=proxies, stream=True)
52
+ zip_file_path = backup_dir+'/master.zip'
53
+ with open(zip_file_path, 'wb+') as f:
54
+ f.write(r.content)
55
+ dst_path = new_version_dir
56
+ with zipfile.ZipFile(zip_file_path, "r") as zip_ref:
57
+ for zip_info in zip_ref.infolist():
58
+ dst_file_path = os.path.join(dst_path, zip_info.filename)
59
+ if os.path.exists(dst_file_path):
60
+ os.remove(dst_file_path)
61
+ zip_ref.extract(zip_info, dst_path)
62
+ return new_version_dir
63
+
64
+
65
+ def patch_and_restart(path):
66
+ """
67
+ 一键更新协议:覆盖和重启
68
+ """
69
+ from distutils import dir_util
70
+ import shutil
71
+ import os
72
+ import sys
73
+ import time
74
+ import glob
75
+ from colorful import print亮黄, print亮绿, print亮红
76
+ # if not using config_private, move origin config.py as config_private.py
77
+ if not os.path.exists('config_private.py'):
78
+ print亮黄('由于您没有设置config_private.py私密配置,现将您的现有配置移动至config_private.py以防止配置丢失,',
79
+ '另外您可以随时在history子文件夹下找回旧版的程序。')
80
+ shutil.copyfile('config.py', 'config_private.py')
81
+ path_new_version = glob.glob(path + '/*-master')[0]
82
+ dir_util.copy_tree(path_new_version, './')
83
+ print亮绿('代码已经更新,即将更新pip包依赖……')
84
+ for i in reversed(range(5)): time.sleep(1); print(i)
85
+ try:
86
+ import subprocess
87
+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', '-r', 'requirements.txt'])
88
+ except:
89
+ print亮红('pip包依赖安装出现问题,需要手动安装新增的依赖库 `python -m pip install -r requirements.txt`,然后在用常规的`python main.py`的方式启动。')
90
+ print亮绿('更新完成,您可以随时在history子文件夹下找回旧版的程序,5s之后重启')
91
+ print亮红('假如重启失败,您可能需要手动安装新增的依赖库 `python -m pip install -r requirements.txt`,然后在用常规的`python main.py`的方式启动。')
92
+ print(' ------------------------------ -----------------------------------')
93
+ for i in reversed(range(8)): time.sleep(1); print(i)
94
+ os.execl(sys.executable, sys.executable, *sys.argv)
95
+
96
+
97
+ def get_current_version():
98
+ import json
99
+ try:
100
+ with open('./version', 'r', encoding='utf8') as f:
101
+ current_version = json.loads(f.read())['version']
102
+ except:
103
+ current_version = ""
104
+ return current_version
105
+
106
+
107
+ def auto_update(raise_error=False):
108
+ """
109
+ 一键更新协议:查询版本和用户意见
110
+ """
111
+ try:
112
+ from toolbox import get_conf
113
+ import requests
114
+ import time
115
+ import json
116
+ proxies, = get_conf('proxies')
117
+ response = requests.get(
118
+ "https://raw.githubusercontent.com/binary-husky/chatgpt_academic/master/version", proxies=proxies, timeout=5)
119
+ remote_json_data = json.loads(response.text)
120
+ remote_version = remote_json_data['version']
121
+ if remote_json_data["show_feature"]:
122
+ new_feature = "新功能:" + remote_json_data["new_feature"]
123
+ else:
124
+ new_feature = ""
125
+ with open('./version', 'r', encoding='utf8') as f:
126
+ current_version = f.read()
127
+ current_version = json.loads(current_version)['version']
128
+ if (remote_version - current_version) >= 0.01-1e-5:
129
+ from colorful import print亮黄
130
+ print亮黄(
131
+ f'\n新版本可用。新版本:{remote_version},当前版本:{current_version}。{new_feature}')
132
+ print('(1)Github更新地址:\nhttps://github.com/binary-husky/chatgpt_academic\n')
133
+ user_instruction = input('(2)是否一键更新代码(Y+回车=确认,输入其他/无输入+回车=不更新)?')
134
+ if user_instruction in ['Y', 'y']:
135
+ path = backup_and_download(current_version, remote_version)
136
+ try:
137
+ patch_and_restart(path)
138
+ except:
139
+ msg = '更新失败。'
140
+ if raise_error:
141
+ from toolbox import trimmed_format_exc
142
+ msg += trimmed_format_exc()
143
+ print(msg)
144
+ else:
145
+ print('自动更新程序:已禁用')
146
+ return
147
+ else:
148
+ return
149
+ except:
150
+ msg = '自动更新程序:已禁用。建议排查:代理网络配置。'
151
+ if raise_error:
152
+ from toolbox import trimmed_format_exc
153
+ msg += trimmed_format_exc()
154
+ print(msg)
155
+
156
+ def warm_up_modules():
157
+ print('正在执行一些模块的预热...')
158
+ from request_llm.bridge_all import model_info
159
+ enc = model_info["gpt-3.5-turbo"]['tokenizer']
160
+ enc.encode("模块预热", disallowed_special=())
161
+ enc = model_info["gpt-4"]['tokenizer']
162
+ enc.encode("模块预热", disallowed_special=())
163
+
164
+ if __name__ == '__main__':
165
+ import os
166
+ os.environ['no_proxy'] = '*' # 避免代理网络产生意外污染
167
+ from toolbox import get_conf
168
+ proxies, = get_conf('proxies')
169
+ check_proxy(proxies)
colorful.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import platform
2
+ from sys import stdout
3
+
4
+ if platform.system()=="Linux":
5
+ pass
6
+ else:
7
+ from colorama import init
8
+ init()
9
+
10
+ # Do you like the elegance of Chinese characters?
11
+ def print红(*kw,**kargs):
12
+ print("\033[0;31m",*kw,"\033[0m",**kargs)
13
+ def print绿(*kw,**kargs):
14
+ print("\033[0;32m",*kw,"\033[0m",**kargs)
15
+ def print黄(*kw,**kargs):
16
+ print("\033[0;33m",*kw,"\033[0m",**kargs)
17
+ def print蓝(*kw,**kargs):
18
+ print("\033[0;34m",*kw,"\033[0m",**kargs)
19
+ def print紫(*kw,**kargs):
20
+ print("\033[0;35m",*kw,"\033[0m",**kargs)
21
+ def print靛(*kw,**kargs):
22
+ print("\033[0;36m",*kw,"\033[0m",**kargs)
23
+
24
+ def print亮红(*kw,**kargs):
25
+ print("\033[1;31m",*kw,"\033[0m",**kargs)
26
+ def print亮绿(*kw,**kargs):
27
+ print("\033[1;32m",*kw,"\033[0m",**kargs)
28
+ def print亮黄(*kw,**kargs):
29
+ print("\033[1;33m",*kw,"\033[0m",**kargs)
30
+ def print亮蓝(*kw,**kargs):
31
+ print("\033[1;34m",*kw,"\033[0m",**kargs)
32
+ def print亮紫(*kw,**kargs):
33
+ print("\033[1;35m",*kw,"\033[0m",**kargs)
34
+ def print亮靛(*kw,**kargs):
35
+ print("\033[1;36m",*kw,"\033[0m",**kargs)
36
+
37
+ # Do you like the elegance of Chinese characters?
38
+ def sprint红(*kw):
39
+ return "\033[0;31m"+' '.join(kw)+"\033[0m"
40
+ def sprint绿(*kw):
41
+ return "\033[0;32m"+' '.join(kw)+"\033[0m"
42
+ def sprint黄(*kw):
43
+ return "\033[0;33m"+' '.join(kw)+"\033[0m"
44
+ def sprint蓝(*kw):
45
+ return "\033[0;34m"+' '.join(kw)+"\033[0m"
46
+ def sprint紫(*kw):
47
+ return "\033[0;35m"+' '.join(kw)+"\033[0m"
48
+ def sprint靛(*kw):
49
+ return "\033[0;36m"+' '.join(kw)+"\033[0m"
50
+ def sprint亮红(*kw):
51
+ return "\033[1;31m"+' '.join(kw)+"\033[0m"
52
+ def sprint亮绿(*kw):
53
+ return "\033[1;32m"+' '.join(kw)+"\033[0m"
54
+ def sprint亮黄(*kw):
55
+ return "\033[1;33m"+' '.join(kw)+"\033[0m"
56
+ def sprint亮蓝(*kw):
57
+ return "\033[1;34m"+' '.join(kw)+"\033[0m"
58
+ def sprint亮紫(*kw):
59
+ return "\033[1;35m"+' '.join(kw)+"\033[0m"
60
+ def sprint亮靛(*kw):
61
+ return "\033[1;36m"+' '.join(kw)+"\033[0m"
config.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 以下所有配置也都支持利用环境变量覆写,环境变量配置格式见docker-compose.yml。
3
+ 读取优先级:环境变量 > config_private.py > config.py
4
+ --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
5
+ All the following configurations also support using environment variables to override,
6
+ and the environment variable configuration format can be seen in docker-compose.yml.
7
+ Configuration reading priority: environment variable > config_private.py > config.py
8
+ """
9
+
10
+ # [step 1]>> API_KEY = "sk-123456789xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789"。极少数情况下,还需要填写组织(格式如org-123456789abcdefghijklmno的),请向下翻,找 API_ORG 设置项
11
+ API_KEY = "此处填API密钥" # 可同时填写多个API-KEY,用英文逗号分割,例如API_KEY = "sk-openaikey1,sk-openaikey2,fkxxxx-api2dkey3,azure-apikey4"
12
+
13
+
14
+ # [step 1]>> API_KEY = "sk-123456789xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx123456789"。极少数情况下,还需要填写组织(格式如org-123456789abcdefghijklmno的),请向下翻,找 API_ORG 设置项
15
+ API_KEY = "此处填API密钥" # 可同时填写多个API-KEY,用英文逗号分割,例如API_KEY = "sk-openaikey1,sk-openaikey2,fkxxxx-api2dkey3,azure-apikey4"
16
+
17
+
18
+ # [step 2]>> 改为True应用代理,如果直接在海外服务器部署,此处不修改;如果使用本地或无地域限制的大模型时,此处也不需要修改
19
+ USE_PROXY = False
20
+ if USE_PROXY:
21
+ """
22
+ 填写格式是 [协议]:// [地址] :[端口],填写之前不要忘记把USE_PROXY改成True,如果直接在海外服务器部署,此处不修改
23
+ <配置教程&视频教程> https://github.com/binary-husky/gpt_academic/issues/1>
24
+ [协议] 常见协议无非socks5h/http; 例如 v2**y 和 ss* 的默认本地协议是socks5h; 而cl**h 的默认本地协议是http
25
+ [地址] 懂的都懂,不懂就填localhost或者127.0.0.1肯定错不了(localhost意思是代理软件安装在本机上)
26
+ [端口] 在代理软件的设置里找。虽然不同的代理软件界面不一样,但端口号都应该在最显眼的位置上
27
+ """
28
+ # 代理网络的地址,打开你的*学*网软件查看代理的协议(socks5h / http)、地址(localhost)和端口(11284)
29
+ proxies = {
30
+ # [协议]:// [地址] :[端口]
31
+ "http": "socks5h://localhost:11284", # 再例如 "http": "http://127.0.0.1:7890",
32
+ "https": "socks5h://localhost:11284", # 再例如 "https": "http://127.0.0.1:7890",
33
+ }
34
+ else:
35
+ proxies = None
36
+
37
+ # ------------------------------------ 以下配置可以优化体验, 但大部分场合下并不需要修改 ------------------------------------
38
+
39
+ # 重新URL重新定向,实现更换API_URL的作用(高危设置! 常规情况下不要修改! 通过修改此设置,您将把您的API-KEY和对话隐私完全暴露给您设定的中间人!)
40
+ # 格式: API_URL_REDIRECT = {"https://api.openai.com/v1/chat/completions": "在这里填写重定向的api.openai.com的URL"}
41
+ # 举例: API_URL_REDIRECT = {"https://api.openai.com/v1/chat/completions": "https://reverse-proxy-url/v1/chat/completions"}
42
+ API_URL_REDIRECT = {}
43
+
44
+
45
+ # 多线程函数插件中,默认允许多少路线程同时访问OpenAI。Free trial users的限制是每分钟3次,Pay-as-you-go users的限制是每分钟3500次
46
+ # 一言以蔽之:免费(5刀)用户填3,OpenAI绑了信用卡的用户可以填 16 或者更高。提高限制请查询:https://platform.openai.com/docs/guides/rate-limits/overview
47
+ DEFAULT_WORKER_NUM = 3
48
+
49
+
50
+ # 色彩主题,可选 ["Default", "Chuanhu-Small-and-Beautiful", "High-Contrast"]
51
+ THEME = "Chuanhu-Small-and-Beautiful"
52
+
53
+
54
+ # 对话窗的高度 (仅在LAYOUT="TOP-DOWN"时生效)
55
+ CHATBOT_HEIGHT = 1115
56
+
57
+
58
+ # 代码高亮
59
+ CODE_HIGHLIGHT = True
60
+
61
+
62
+ # 窗口布局
63
+ LAYOUT = "LEFT-RIGHT" # "LEFT-RIGHT"(左右布局) # "TOP-DOWN"(上下布局)
64
+ DARK_MODE = True # 暗色模式 / 亮色模式
65
+
66
+
67
+ # 发送请求到OpenAI后,等待多久判定为超时
68
+ TIMEOUT_SECONDS = 30
69
+
70
+
71
+ # 网页的端口, -1代表随机端口
72
+ WEB_PORT = -1
73
+
74
+
75
+ # 如果OpenAI不响应(网络卡顿、代理失败、KEY失效),重试的次数限制
76
+ MAX_RETRY = 2
77
+
78
+ # OpenAI模型选择是(gpt4现在只对申请成功的人开放)
79
+ LLM_MODEL = "gpt-3.5-turbo" # 可选 "chatglm"
80
+ AVAIL_LLM_MODELS = ["gpt-3.5-turbo", "gpt-4", "api2d-gpt-4", "api2d-gpt-3.5-turbo", "spark", "azure-gpt-3.5"]
81
+
82
+ # 插件分类默认选项
83
+ DEFAULT_FN_GROUPS = ['对话', '编程', '学术']
84
+
85
+
86
+ # 模型选择是 (注意: LLM_MODEL是默认选中的模型, 它*必须*被包含在AVAIL_LLM_MODELS列表中 )
87
+ LLM_MODEL = "gpt-3.5-turbo" # 可选 ↓↓↓
88
+ AVAIL_LLM_MODELS = ["gpt-3.5-turbo-16k", "gpt-3.5-turbo", "azure-gpt-3.5", "api2d-gpt-3.5-turbo",
89
+ "gpt-4", "api2d-gpt-4", "chatglm", "moss", "newbing", "stack-claude"]
90
+ # P.S. 其他可用的模型还包括 ["qianfan", "llama2", "qwen", "gpt-3.5-turbo-0613", "gpt-3.5-turbo-16k-0613",
91
+ # "spark", "sparkv2", "chatglm_onnx", "claude-1-100k", "claude-2", "internlm", "jittorllms_pangualpha", "jittorllms_llama"]
92
+
93
+
94
+ # 百度千帆(LLM_MODEL="qianfan")
95
+ BAIDU_CLOUD_API_KEY = ''
96
+ BAIDU_CLOUD_SECRET_KEY = ''
97
+ BAIDU_CLOUD_QIANFAN_MODEL = 'ERNIE-Bot' # 可选 "ERNIE-Bot"(文心一言), "ERNIE-Bot-turbo", "BLOOMZ-7B", "Llama-2-70B-Chat", "Llama-2-13B-Chat", "Llama-2-7B-Chat"
98
+
99
+
100
+ # 如果使用ChatGLM2微调模型,请把 LLM_MODEL="chatglmft",并在此处指定模型路径
101
+ CHATGLM_PTUNING_CHECKPOINT = "" # 例如"/home/hmp/ChatGLM2-6B/ptuning/output/6b-pt-128-1e-2/checkpoint-100"
102
+
103
+
104
+ # 本地LLM模型如ChatGLM的执行方式 CPU/GPU
105
+ LOCAL_MODEL_DEVICE = "cpu" # 可选 "cuda"
106
+ LOCAL_MODEL_QUANT = "FP16" # 默认 "FP16" "INT4" 启用量化INT4版本 "INT8" 启用量化INT8版本
107
+
108
+
109
+ # 设置gradio的并行线程数(不需要修改)
110
+ CONCURRENT_COUNT = 100
111
+
112
+
113
+ # 是否在提交时自动清空输入框
114
+ AUTO_CLEAR_TXT = False
115
+
116
+
117
+ # 加一个live2d装饰
118
+ ADD_WAIFU = False
119
+
120
+
121
+ # 设置用户名和密码(不需要修改)(相关功能不稳定,与gradio版本和网络都相关,如果本地使用不建议加这个)
122
+ # [("username", "password"), ("username2", "password2"), ...]
123
+ AUTHENTICATION = []
124
+
125
+
126
+ # 如果需要在二级路径下运行(常规情况下,不要修改!!)(需要配合修改main.py才能生效!)
127
+ CUSTOM_PATH = "/"
128
+
129
+
130
+ # 极少数情况下,openai的官方KEY需要伴随组织编码(格式如org-xxxxxxxxxxxxxxxxxxxxxxxx)使用
131
+ API_ORG = ""
132
+
133
+
134
+ # 如果需要使用Slack Claude,使用教程详情见 request_llm/README.md
135
+ SLACK_CLAUDE_BOT_ID = ''
136
+ SLACK_CLAUDE_USER_TOKEN = ''
137
+
138
+
139
+ # 如果需要使用AZURE 详情请见额外文档 docs\use_azure.md
140
+ AZURE_ENDPOINT = "https://你亲手写的api名称.openai.azure.com/"
141
+ AZURE_API_KEY = "填入azure openai api的密钥" # 建议直接在API_KEY处填写,该选项即将被弃用
142
+ AZURE_ENGINE = "填入你亲手写的部署名" # 读 docs\use_azure.md
143
+
144
+
145
+ # 使用Newbing
146
+ NEWBING_STYLE = "creative" # ["creative", "balanced", "precise"]
147
+ NEWBING_COOKIES = """
148
+ put your new bing cookies here
149
+ """
150
+
151
+
152
+ # 阿里云实时语音识别 配置难度较高 仅建议高手用户使用 参考 https://github.com/binary-husky/gpt_academic/blob/master/docs/use_audio.md
153
+ ENABLE_AUDIO = False
154
+ ALIYUN_TOKEN="" # 例如 f37f30e0f9934c34a992f6f64f7eba4f
155
+ ALIYUN_APPKEY="" # 例如 RoPlZrM88DnAFkZK
156
+ ALIYUN_ACCESSKEY="" # (无需填写)
157
+ ALIYUN_SECRET="" # (无需填写)
158
+
159
+
160
+ # 接入讯飞星火大模型 https://console.xfyun.cn/services/iat
161
+ XFYUN_APPID = "00000000"
162
+ XFYUN_API_SECRET = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
163
+ XFYUN_API_KEY = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
164
+
165
+
166
+ # Claude API KEY
167
+ ANTHROPIC_API_KEY = ""
168
+
169
+
170
+ # 自定义API KEY格式
171
+ CUSTOM_API_KEY_PATTERN = ""
172
+
173
+
174
+ # HUGGINGFACE的TOKEN,下载LLAMA时起作用 https://huggingface.co/docs/hub/security-tokens
175
+ HUGGINGFACE_ACCESS_TOKEN = "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"
176
+
177
+
178
+ # GROBID服务器地址(填写多个可以均衡负载),用于高质量地读取PDF文档
179
+ # 获取方法:复制以下空间https://huggingface.co/spaces/qingxu98/grobid,设为public,然后GROBID_URL = "https://(你的hf用户名如qingxu98)-(你的填写的空间名如grobid).hf.space"
180
+ GROBID_URLS = [
181
+ "https://qingxu98-grobid.hf.space","https://qingxu98-grobid2.hf.space","https://qingxu98-grobid3.hf.space",
182
+ "https://shaocongma-grobid.hf.space","https://FBR123-grobid.hf.space", "https://yeku-grobid.hf.space",
183
+ ]
184
+
185
+
186
+ # 是否允许通过自然语言描述修改本页的配置,该功能具有一定的危险性,默认关闭
187
+ ALLOW_RESET_CONFIG = False
188
+
189
+
190
+ """
191
+ 在线大模型配置关联关系示意图
192
+
193
+ ├── "gpt-3.5-turbo" 等openai模型
194
+ │ ├── API_KEY
195
+ │ ├── CUSTOM_API_KEY_PATTERN(不常用)
196
+ │ ├── API_ORG(不常用)
197
+ │ └── API_URL_REDIRECT(不常用)
198
+
199
+ ├── "azure-gpt-3.5" 等azure模型
200
+ │ ├── API_KEY
201
+ │ ├── AZURE_ENDPOINT
202
+ │ ├── AZURE_API_KEY
203
+ │ ├── AZURE_ENGINE
204
+ │ └── API_URL_REDIRECT
205
+
206
+ ├── "spark" 星火认知大模型 spark & sparkv2
207
+ │ ├── XFYUN_APPID
208
+ │ ├── XFYUN_API_SECRET
209
+ │ └── XFYUN_API_KEY
210
+
211
+ ├── "claude-1-100k" 等claude模型
212
+ │ └── ANTHROPIC_API_KEY
213
+
214
+ ├── "stack-claude"
215
+ │ ├── SLACK_CLAUDE_BOT_ID
216
+ │ └── SLACK_CLAUDE_USER_TOKEN
217
+
218
+ ├── "qianfan" 百度千帆大模型库
219
+ │ ├── BAIDU_CLOUD_QIANFAN_MODEL
220
+ │ ├── BAIDU_CLOUD_API_KEY
221
+ │ └── BAIDU_CLOUD_SECRET_KEY
222
+
223
+ ├── "newbing" Newbing接口不再稳定,不推荐使用
224
+ ├── NEWBING_STYLE
225
+ └── NEWBING_COOKIES
226
+
227
+
228
+ 用户图形界面布局依赖关系示意图
229
+
230
+ ├── CHATBOT_HEIGHT 对话窗的高度
231
+ ├── CODE_HIGHLIGHT 代码高亮
232
+ ├── LAYOUT 窗口布局
233
+ ├── DARK_MODE 暗色模式 / 亮色模式
234
+ ├── DEFAULT_FN_GROUPS ��件分类默认选项
235
+ ├── THEME 色彩主题
236
+ ├── AUTO_CLEAR_TXT 是否在提交时自动清空输入框
237
+ ├── ADD_WAIFU 加一个live2d装饰
238
+ ├── ALLOW_RESET_CONFIG 是否允许通过自然语言描述修改本页的配置,该功能具有一定的危险性
239
+
240
+
241
+ 插件在线服务配置依赖关系示意图
242
+
243
+ ├── 语音功能
244
+ │ ├── ENABLE_AUDIO
245
+ │ ├── ALIYUN_TOKEN
246
+ │ ├── ALIYUN_APPKEY
247
+ │ ├── ALIYUN_ACCESSKEY
248
+ │ └── ALIYUN_SECRET
249
+
250
+ ├── PDF文档精准解析
251
+ │ └── GROBID_URLS
252
+
253
+ """
core_functional.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 'primary' 颜色对应 theme.py 中的 primary_hue
2
+ # 'secondary' 颜色对应 theme.py 中的 neutral_hue
3
+ # 'stop' 颜色对应 theme.py 中的 color_er
4
+ import importlib
5
+ from toolbox import clear_line_break
6
+
7
+
8
+ def get_core_functions():
9
+ return {
10
+ "英语学术润色": {
11
+ # 前缀,会被加在你的输入之前。例如,用来描述你的要求,例如翻译、解释代码、润色等等
12
+ "Prefix": r"Below is a paragraph from an academic paper. Polish the writing to meet the academic style, " +
13
+ r"improve the spelling, grammar, clarity, concision and overall readability. When necessary, rewrite the whole sentence. " +
14
+ r"Furthermore, list all modification and explain the reasons to do so in markdown table." + "\n\n",
15
+ # 后缀,会被加在你的输入之后。例如,配合前缀可以把你的输入内容用引号圈起来
16
+ "Suffix": r"",
17
+ # 按钮颜色 (默认 secondary)
18
+ "Color": r"secondary",
19
+ # 按钮是否可见 (默认 True,即可见)
20
+ "Visible": True,
21
+ # 是否在触发时清除历史 (默认 False,即不处理之前的对话历史)
22
+ "AutoClearHistory": False
23
+ },
24
+ "中文学术润色": {
25
+ "Prefix": r"作为一名中文学术论文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性," +
26
+ r"同时分解长句,减少重复,并提供改进建议。请只提供文本的更正版本,避免包括解释。请编辑以下文本" + "\n\n",
27
+ "Suffix": r"",
28
+ },
29
+ "查找语法错误": {
30
+ "Prefix": r"Can you help me ensure that the grammar and the spelling is correct? " +
31
+ r"Do not try to polish the text, if no mistake is found, tell me that this paragraph is good." +
32
+ r"If you find grammar or spelling mistakes, please list mistakes you find in a two-column markdown table, " +
33
+ r"put the original text the first column, " +
34
+ r"put the corrected text in the second column and highlight the key words you fixed.""\n"
35
+ r"Example:""\n"
36
+ r"Paragraph: How is you? Do you knows what is it?""\n"
37
+ r"| Original sentence | Corrected sentence |""\n"
38
+ r"| :--- | :--- |""\n"
39
+ r"| How **is** you? | How **are** you? |""\n"
40
+ r"| Do you **knows** what **is** **it**? | Do you **know** what **it** **is** ? |""\n"
41
+ r"Below is a paragraph from an academic paper. "
42
+ r"You need to report all grammar and spelling mistakes as the example before."
43
+ + "\n\n",
44
+ "Suffix": r"",
45
+ "PreProcess": clear_line_break, # 预处理:清除换行符
46
+ },
47
+ "中译英": {
48
+ "Prefix": r"Please translate following sentence to English:" + "\n\n",
49
+ "Suffix": r"",
50
+ },
51
+ "学术中英互译": {
52
+ "Prefix": r"I want you to act as a scientific English-Chinese translator, " +
53
+ r"I will provide you with some paragraphs in one language " +
54
+ r"and your task is to accurately and academically translate the paragraphs only into the other language. " +
55
+ r"Do not repeat the original provided paragraphs after translation. " +
56
+ r"You should use artificial intelligence tools, " +
57
+ r"such as natural language processing, and rhetorical knowledge " +
58
+ r"and experience about effective writing techniques to reply. " +
59
+ r"I'll give you my paragraphs as follows, tell me what language it is written in, and then translate:" + "\n\n",
60
+ "Suffix": "",
61
+ "Color": "secondary",
62
+ },
63
+ "英译中": {
64
+ "Prefix": r"翻译成地道的中文:" + "\n\n",
65
+ "Suffix": r"",
66
+ "Visible": False,
67
+ },
68
+ "找图片": {
69
+ "Prefix": r"我需要你找一张网络图片。使用Unsplash API(https://source.unsplash.com/960x640/?<英语关键词>)获取图片URL," +
70
+ r"然后请使用Markdown格式封装,并且不要有反斜线,不要用代码块。现在,请按以下描述给我发送图片:" + "\n\n",
71
+ "Suffix": r"",
72
+ "Visible": False,
73
+ },
74
+ "解释代码": {
75
+ "Prefix": r"请解释以下代码:" + "\n```\n",
76
+ "Suffix": "\n```\n",
77
+ },
78
+ "参考文献转Bib": {
79
+ "Prefix": r"Here are some bibliography items, please transform them into bibtex style." +
80
+ r"Note that, reference styles maybe more than one kind, you should transform each item correctly." +
81
+ r"Items need to be transformed:",
82
+ "Visible": False,
83
+ "Suffix": r"",
84
+ }
85
+ }
86
+
87
+
88
+ def handle_core_functionality(additional_fn, inputs, history, chatbot):
89
+ import core_functional
90
+ importlib.reload(core_functional) # 热更新prompt
91
+ core_functional = core_functional.get_core_functions()
92
+ if "PreProcess" in core_functional[additional_fn]: inputs = core_functional[additional_fn]["PreProcess"](inputs) # 获取预处理函数(如果有的话)
93
+ inputs = core_functional[additional_fn]["Prefix"] + inputs + core_functional[additional_fn]["Suffix"]
94
+ if core_functional[additional_fn].get("AutoClearHistory", False):
95
+ history = []
96
+ return inputs, history
crazy_functional.py ADDED
@@ -0,0 +1,564 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import HotReload # HotReload 的意思是热更新,修改函数插件后,不需要重启程序,代码直接生效
2
+
3
+
4
+ def get_crazy_functions():
5
+ from crazy_functions.读文章写摘要 import 读文章写摘要
6
+ from crazy_functions.生成函数注释 import 批量生成函数注释
7
+ from crazy_functions.解析项目源代码 import 解析项目本身
8
+ from crazy_functions.解析项目源代码 import 解析一个Python项目
9
+ from crazy_functions.解析项目源代码 import 解析一个C项目的头文件
10
+ from crazy_functions.解析项目源代码 import 解析一个C项目
11
+ from crazy_functions.解析项目源代码 import 解析一个Golang项目
12
+ from crazy_functions.解析项目源代码 import 解析一个Rust项目
13
+ from crazy_functions.解析项目源代码 import 解析一个Java项目
14
+ from crazy_functions.解析项目源代码 import 解析一个前端项目
15
+ from crazy_functions.高级功能函数模板 import 高阶功能模板函数
16
+ from crazy_functions.代码重写为全英文_多线程 import 全项目切换英文
17
+ from crazy_functions.Latex全文润色 import Latex英文润色
18
+ from crazy_functions.询问多个大语言模型 import 同时问询
19
+ from crazy_functions.解析项目源代码 import 解析一个Lua项目
20
+ from crazy_functions.解析项目源代码 import 解析一个CSharp项目
21
+ from crazy_functions.总结word文档 import 总结word文档
22
+ from crazy_functions.解析JupyterNotebook import 解析ipynb文件
23
+ from crazy_functions.对话历史存档 import 对话历史存档
24
+ from crazy_functions.对话历史存档 import 载入对话历史存档
25
+ from crazy_functions.对话历史存档 import 删除所有本地对话历史记录
26
+ from crazy_functions.辅助功能 import 清除缓存
27
+ from crazy_functions.批量Markdown翻译 import Markdown英译中
28
+ from crazy_functions.批量总结PDF文档 import 批量总结PDF文档
29
+ from crazy_functions.批量翻译PDF文档_多线程 import 批量翻译PDF文档
30
+ from crazy_functions.谷歌检索小助手 import 谷歌检索小助手
31
+ from crazy_functions.理解PDF文档内容 import 理解PDF文档内容标准文件输入
32
+ from crazy_functions.Latex全文润色 import Latex中文润色
33
+ from crazy_functions.Latex全文润色 import Latex英文纠错
34
+ from crazy_functions.Latex全文翻译 import Latex中译英
35
+ from crazy_functions.Latex全文翻译 import Latex英译中
36
+ from crazy_functions.批量Markdown翻译 import Markdown中译英
37
+ from crazy_functions.虚空终端 import 虚空终端
38
+
39
+
40
+ function_plugins = {
41
+ "虚空终端": {
42
+ "Group": "对话|编程|学术",
43
+ "Color": "stop",
44
+ "AsButton": True,
45
+ "Function": HotReload(虚空终端)
46
+ },
47
+ "解析整个Python项目": {
48
+ "Group": "编程",
49
+ "Color": "stop",
50
+ "AsButton": True,
51
+ "Info": "解析一个Python项目的所有源文件(.py) | 输入参数为路径",
52
+ "Function": HotReload(解析一个Python项目)
53
+ },
54
+ "载入对话历史存档(先上传存档或输入路径)": {
55
+ "Group": "对话",
56
+ "Color": "stop",
57
+ "AsButton": False,
58
+ "Info": "载入对话历史存档 | 输入参数为路径",
59
+ "Function": HotReload(载入对话历史存档)
60
+ },
61
+ "删除所有本地对话历史记录(谨慎操作)": {
62
+ "Group": "对话",
63
+ "AsButton": False,
64
+ "Info": "删除所有本地对话历史记录,谨慎操作 | 不需要输入参数",
65
+ "Function": HotReload(删除所有本地对话历史记录)
66
+ },
67
+ "清除所有缓存文件(谨慎操作)": {
68
+ "Group": "对话",
69
+ "Color": "stop",
70
+ "AsButton": False, # 加入下拉菜单中
71
+ "Info": "清除所有缓存文件,谨慎操作 | 不需要输入参数",
72
+ "Function": HotReload(清除缓存)
73
+ },
74
+ "批量总结Word文档": {
75
+ "Group": "学术",
76
+ "Color": "stop",
77
+ "AsButton": True,
78
+ "Info": "批量总结word文档 | 输入参数为路径",
79
+ "Function": HotReload(总结word文档)
80
+ },
81
+ "解析整个C++项目头文件": {
82
+ "Group": "编程",
83
+ "Color": "stop",
84
+ "AsButton": False, # 加入下拉菜单中
85
+ "Info": "解析一个C++项目的所有头文件(.h/.hpp) | 输入参数为路径",
86
+ "Function": HotReload(解析一个C项目的头文件)
87
+ },
88
+ "解析整个C++项目(.cpp/.hpp/.c/.h)": {
89
+ "Group": "编程",
90
+ "Color": "stop",
91
+ "AsButton": False, # 加入下拉菜单中
92
+ "Info": "解析一个C++项目的所有源文件(.cpp/.hpp/.c/.h)| 输入参数为路径",
93
+ "Function": HotReload(解析一个C项目)
94
+ },
95
+ "解析整个Go项目": {
96
+ "Group": "编程",
97
+ "Color": "stop",
98
+ "AsButton": False, # 加入下拉菜单中
99
+ "Info": "解析一个Go项目的所有源文件 | 输入参数为路径",
100
+ "Function": HotReload(解析一个Golang项目)
101
+ },
102
+ "解析整个Rust项目": {
103
+ "Group": "编程",
104
+ "Color": "stop",
105
+ "AsButton": False, # 加入下拉菜单中
106
+ "Info": "解析一个Rust项目的所有源文件 | 输入参数为路径",
107
+ "Function": HotReload(解析一个Rust项目)
108
+ },
109
+ "解析整个Java项目": {
110
+ "Group": "编程",
111
+ "Color": "stop",
112
+ "AsButton": False, # 加入下拉菜单中
113
+ "Info": "解析一个Java项目的所有源文件 | 输入参数为路径",
114
+ "Function": HotReload(解析一个Java项目)
115
+ },
116
+ "解析整个前端项目(js,ts,css等)": {
117
+ "Group": "编程",
118
+ "Color": "stop",
119
+ "AsButton": False, # 加入下拉菜单中
120
+ "Info": "解析一个前端项目的所有源文件(js,ts,css等) | 输入参数为路径",
121
+ "Function": HotReload(解析一个前端项目)
122
+ },
123
+ "解析整个Lua项目": {
124
+ "Group": "编程",
125
+ "Color": "stop",
126
+ "AsButton": False, # 加入下拉菜单中
127
+ "Info": "解析一个Lua项目的所有源文件 | 输入参数为路径",
128
+ "Function": HotReload(解析一个Lua项目)
129
+ },
130
+ "解析整个CSharp项目": {
131
+ "Group": "编程",
132
+ "Color": "stop",
133
+ "AsButton": False, # 加入下拉菜单中
134
+ "Info": "解析一个CSharp项目的所有源文件 | 输入参数为路径",
135
+ "Function": HotReload(解析一个CSharp项目)
136
+ },
137
+ "解析Jupyter Notebook文件": {
138
+ "Group": "编程",
139
+ "Color": "stop",
140
+ "AsButton": False,
141
+ "Info": "解析Jupyter Notebook文件 | 输入参数为路径",
142
+ "Function": HotReload(解析ipynb文件),
143
+ "AdvancedArgs": True, # 调用时,唤起高级参数输入区(默认False)
144
+ "ArgsReminder": "若输入0,则不解析notebook中的Markdown块", # 高级参数输入区的显示提示
145
+ },
146
+ "读Tex论文写摘要": {
147
+ "Group": "学术",
148
+ "Color": "stop",
149
+ "AsButton": False,
150
+ "Info": "读取Tex论文并写摘要 | 输入参数为路径",
151
+ "Function": HotReload(读文章写摘要)
152
+ },
153
+ "翻译README或MD": {
154
+ "Group": "编程",
155
+ "Color": "stop",
156
+ "AsButton": True,
157
+ "Info": "将Markdown翻译为中文 | 输入参数为路径或URL",
158
+ "Function": HotReload(Markdown英译中)
159
+ },
160
+ "翻译Markdown或README(支持Github链接)": {
161
+ "Group": "编程",
162
+ "Color": "stop",
163
+ "AsButton": False,
164
+ "Info": "将Markdown或README翻译为中文 | 输入参数为路径或URL",
165
+ "Function": HotReload(Markdown英译中)
166
+ },
167
+ "批量生成函数注释": {
168
+ "Group": "编程",
169
+ "Color": "stop",
170
+ "AsButton": False, # 加入下拉菜单中
171
+ "Info": "批量生成函数的注释 | 输入参数为路径",
172
+ "Function": HotReload(批量生成函数注释)
173
+ },
174
+ "保存当前的对话": {
175
+ "Group": "对话",
176
+ "AsButton": True,
177
+ "Info": "保存当前的对话 | 不需要输入参数",
178
+ "Function": HotReload(对话历史存档)
179
+ },
180
+ "[多线程Demo]解析此项目本身(源码自译解)": {
181
+ "Group": "对话|编程",
182
+ "AsButton": False, # 加入下拉菜单中
183
+ "Info": "多线程解析并翻译此项目的源码 | 不需要输入参数",
184
+ "Function": HotReload(解析项目本身)
185
+ },
186
+ "[插件demo]历史上的今天": {
187
+ "Group": "对话",
188
+ "AsButton": True,
189
+ "Info": "查看历史上的今天事件 | 不需要输入参数",
190
+ "Function": HotReload(高阶功能模板函数)
191
+ },
192
+ "精准翻译PDF论文": {
193
+ "Group": "学术",
194
+ "Color": "stop",
195
+ "AsButton": True,
196
+ "Info": "精准翻译PDF论文为中文 | 输入参数为路径",
197
+ "Function": HotReload(批量翻译PDF文档)
198
+ },
199
+ "询问多个GPT模型": {
200
+ "Group": "对话",
201
+ "Color": "stop",
202
+ "AsButton": True,
203
+ "Function": HotReload(同时问询)
204
+ },
205
+ "批量总结PDF文档": {
206
+ "Group": "学术",
207
+ "Color": "stop",
208
+ "AsButton": False, # 加入下拉菜单中
209
+ "Info": "批量总结PDF文档的内容 | 输入参���为路径",
210
+ "Function": HotReload(批量总结PDF文档)
211
+ },
212
+ "谷歌学术检索助手(输入谷歌学术搜索页url)": {
213
+ "Group": "学术",
214
+ "Color": "stop",
215
+ "AsButton": False, # 加入下拉菜单中
216
+ "Info": "使用谷歌学术检索助手搜索指定URL的结果 | 输入参数为谷歌学术搜索页的URL",
217
+ "Function": HotReload(谷歌检索小助手)
218
+ },
219
+ "理解PDF文档内容 (模仿ChatPDF)": {
220
+ "Group": "学术",
221
+ "Color": "stop",
222
+ "AsButton": False, # 加入下拉菜单中
223
+ "Info": "理解PDF文档的内容并进行回答 | 输入参数为路径",
224
+ "Function": HotReload(理解PDF文档内容标准文件输入)
225
+ },
226
+ "英文Latex项目全文润色(输入路径或上传压缩包)": {
227
+ "Group": "学术",
228
+ "Color": "stop",
229
+ "AsButton": False, # 加入下拉菜单中
230
+ "Info": "对英文Latex项目全文进行润色处理 | 输入参数为路径或上传压缩包",
231
+ "Function": HotReload(Latex英文润色)
232
+ },
233
+ "英文Latex项目全文纠错(输入路径或上传压缩包)": {
234
+ "Group": "学术",
235
+ "Color": "stop",
236
+ "AsButton": False, # 加入下拉菜单中
237
+ "Info": "对英文Latex项目全文进行纠错处理 | 输入参数为路径或上传压缩包",
238
+ "Function": HotReload(Latex英文纠错)
239
+ },
240
+ "中文Latex项目全文润色(输入路径或上传压缩包)": {
241
+ "Group": "学术",
242
+ "Color": "stop",
243
+ "AsButton": False, # 加入下拉菜单中
244
+ "Info": "对中文Latex项目全文进行润色处理 | 输入参数为路径或上传压缩包",
245
+ "Function": HotReload(Latex中文润色)
246
+ },
247
+ "Latex项目全文中译英(输入路径或上传压缩包)": {
248
+ "Group": "学术",
249
+ "Color": "stop",
250
+ "AsButton": False, # 加入下拉菜单中
251
+ "Info": "对Latex项目全文进行中译英处理 | 输入参数为路径或上传压缩包",
252
+ "Function": HotReload(Latex中译英)
253
+ },
254
+ "Latex项目全文英译中(输入路径或上传压缩包)": {
255
+ "Group": "学术",
256
+ "Color": "stop",
257
+ "AsButton": False, # 加入下拉菜单中
258
+ "Info": "对Latex项目全文进行英译中处理 | 输入参数为路径或上传压缩包",
259
+ "Function": HotReload(Latex英译中)
260
+ },
261
+ "批量Markdown中译英(输入路径或上传压缩包)": {
262
+ "Group": "编程",
263
+ "Color": "stop",
264
+ "AsButton": False, # 加入下拉菜单中
265
+ "Info": "批量将Markdown文件中文翻译为英文 | 输入参数为路径或上传压缩包",
266
+ "Function": HotReload(Markdown中译英)
267
+ },
268
+ }
269
+
270
+ # -=--=- 尚未充分测试的实验性插件 & 需要额外依赖的插件 -=--=-
271
+ try:
272
+ from crazy_functions.下载arxiv论文翻译摘要 import 下载arxiv论文并翻译摘要
273
+ function_plugins.update({
274
+ "一键下载arxiv论文并翻译摘要(先在input输入编号,如1812.10695)": {
275
+ "Group": "学术",
276
+ "Color": "stop",
277
+ "AsButton": False, # 加入下拉菜单中
278
+ # "Info": "下载arxiv论文并翻译摘要 | 输入参数为arxiv编号如1812.10695",
279
+ "Function": HotReload(下载arxiv论文并翻译摘要)
280
+ }
281
+ })
282
+ except:
283
+ print('Load function plugin failed')
284
+
285
+ try:
286
+ from crazy_functions.联网的ChatGPT import 连接网络回答问题
287
+ function_plugins.update({
288
+ "连接网络回答问题(输入问题后点击该插件,需要访问谷歌)": {
289
+ "Group": "对话",
290
+ "Color": "stop",
291
+ "AsButton": False, # 加入下拉菜单中
292
+ # "Info": "连接网络回答问题(需要访问谷歌)| 输入参数是一个问题",
293
+ "Function": HotReload(连接网络回答问题)
294
+ }
295
+ })
296
+ from crazy_functions.联网的ChatGPT_bing版 import 连接bing搜索回答问题
297
+ function_plugins.update({
298
+ "连接网络回答问题(中文Bing版,输入问题后点击该插件)": {
299
+ "Group": "对话",
300
+ "Color": "stop",
301
+ "AsButton": False, # 加入下拉菜单中
302
+ "Info": "连接网络回答问题(需要访问中文Bing)| 输入参数是一个问题",
303
+ "Function": HotReload(连接bing搜索回答问题)
304
+ }
305
+ })
306
+ except:
307
+ print('Load function plugin failed')
308
+
309
+ try:
310
+ from crazy_functions.解析项目源代码 import 解析任意code项目
311
+ function_plugins.update({
312
+ "解析项目源代码(手动指定和筛选源代码文件类型)": {
313
+ "Group": "编程",
314
+ "Color": "stop",
315
+ "AsButton": False,
316
+ "AdvancedArgs": True, # 调用时,唤起高级参数输入区(默认False)
317
+ "ArgsReminder": "输入时用逗号隔开, *代表通配符, 加了^代表不匹配; 不输入代表全部匹配。例如: \"*.c, ^*.cpp, config.toml, ^*.toml\"", # 高级参数输入区的显示提示
318
+ "Function": HotReload(解析任意code项目)
319
+ },
320
+ })
321
+ except:
322
+ print('Load function plugin failed')
323
+
324
+ try:
325
+ from crazy_functions.询问多个大语言模型 import 同时问询_指定模型
326
+ function_plugins.update({
327
+ "询问多个GPT模型(手动指定询问哪些模型)": {
328
+ "Group": "对话",
329
+ "Color": "stop",
330
+ "AsButton": False,
331
+ "AdvancedArgs": True, # 调用时,唤起高级参数输入区(默认False)
332
+ "ArgsReminder": "支持任意数量的llm接口,用&符号分隔。例如chatglm&gpt-3.5-turbo&api2d-gpt-4", # 高级参数输入区的显示提示
333
+ "Function": HotReload(同时问询_指定模型)
334
+ },
335
+ })
336
+ except:
337
+ print('Load function plugin failed')
338
+
339
+ try:
340
+ from crazy_functions.图片生成 import 图片生成
341
+ function_plugins.update({
342
+ "图片生成(先切换模型到openai或api2d)": {
343
+ "Group": "对话",
344
+ "Color": "stop",
345
+ "AsButton": False,
346
+ "AdvancedArgs": True, # 调用时,唤起高级参数输入区(默认False)
347
+ "ArgsReminder": "在这里输入分辨率, 如256x256(默认)", # 高级参数输入区的显示提示
348
+ "Info": "图片生成 | 输入参数字符串,提供图像的内容",
349
+ "Function": HotReload(图片生成)
350
+ },
351
+ })
352
+ except:
353
+ print('Load function plugin failed')
354
+
355
+ try:
356
+ from crazy_functions.总结音视频 import 总结音视频
357
+ function_plugins.update({
358
+ "批量总结音视频(输入路径或上传压缩包)": {
359
+ "Group": "对话",
360
+ "Color": "stop",
361
+ "AsButton": False,
362
+ "AdvancedArgs": True,
363
+ "ArgsReminder": "调用openai api 使用whisper-1模型, 目前支持的格式:mp4, m4a, wav, mpga, mpeg, mp3。此处可以输入解析提示,例如:解析为简体中文(默认)。",
364
+ "Info": "批量总结音频或视频 | 输入参数为路径",
365
+ "Function": HotReload(总结音视频)
366
+ }
367
+ })
368
+ except:
369
+ print('Load function plugin failed')
370
+
371
+ try:
372
+ from crazy_functions.数学动画生成manim import 动画生成
373
+ function_plugins.update({
374
+ "数学动画生成(Manim)": {
375
+ "Group": "对话",
376
+ "Color": "stop",
377
+ "AsButton": False,
378
+ "Info": "按照自然语言描述生成一个动画 | 输入参数是一段话",
379
+ "Function": HotReload(动画生成)
380
+ }
381
+ })
382
+ except:
383
+ print('Load function plugin failed')
384
+
385
+ try:
386
+ from crazy_functions.批量Markdown翻译 import Markdown翻译指定语言
387
+ function_plugins.update({
388
+ "Markdown翻译(手动指定语言)": {
389
+ "Group": "编程",
390
+ "Color": "stop",
391
+ "AsButton": False,
392
+ "AdvancedArgs": True,
393
+ "ArgsReminder": "请输入要翻译成哪种语言,默认为Chinese。",
394
+ "Function": HotReload(Markdown翻译指定语言)
395
+ }
396
+ })
397
+ except:
398
+ print('Load function plugin failed')
399
+
400
+ try:
401
+ from crazy_functions.Langchain知识库 import 知识库问答
402
+ function_plugins.update({
403
+ "构建知识库(请先上传文件素材)": {
404
+ "Group": "对话",
405
+ "Color": "stop",
406
+ "AsButton": False,
407
+ "AdvancedArgs": True,
408
+ "ArgsReminder": "待注入的知识库名称id, 默认为default",
409
+ "Function": HotReload(知识库问答)
410
+ }
411
+ })
412
+ except:
413
+ print('Load function plugin failed')
414
+
415
+ try:
416
+ from crazy_functions.Langchain知识库 import 读取知识库作答
417
+ function_plugins.update({
418
+ "知识库问答": {
419
+ "Group": "对话",
420
+ "Color": "stop",
421
+ "AsButton": False,
422
+ "AdvancedArgs": True,
423
+ "ArgsReminder": "待提取的知识库名称id, 默认为default, 您需要首先调用构建知识库",
424
+ "Function": HotReload(读取知识库作答)
425
+ }
426
+ })
427
+ except:
428
+ print('Load function plugin failed')
429
+
430
+ try:
431
+ from crazy_functions.交互功能函数模板 import 交互功能模板函数
432
+ function_plugins.update({
433
+ "交互功能模板函数": {
434
+ "Group": "对话",
435
+ "Color": "stop",
436
+ "AsButton": False,
437
+ "Function": HotReload(交互功能模板函数)
438
+ }
439
+ })
440
+ except:
441
+ print('Load function plugin failed')
442
+
443
+ try:
444
+ from crazy_functions.Latex输出PDF结果 import Latex英文纠错加PDF对比
445
+ function_plugins.update({
446
+ "Latex英文纠错+高亮修正位置 [需Latex]": {
447
+ "Group": "学术",
448
+ "Color": "stop",
449
+ "AsButton": False,
450
+ "AdvancedArgs": True,
451
+ "ArgsReminder": "如果有必要, 请在此处追加更细致的矫错指令(使用英文)。",
452
+ "Function": HotReload(Latex英文纠错加PDF对比)
453
+ }
454
+ })
455
+ from crazy_functions.Latex输出PDF结果 import Latex翻译中文并重新编译PDF
456
+ function_plugins.update({
457
+ "Arixv论文精细翻译(输入arxivID)[需Latex]": {
458
+ "Group": "学术",
459
+ "Color": "stop",
460
+ "AsButton": False,
461
+ "AdvancedArgs": True,
462
+ "ArgsReminder":
463
+ "如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 " +
464
+ "例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " +
465
+ 'If the term "agent" is used in this section, it should be translated to "智能体". ',
466
+ "Info": "Arixv论文精细翻译 | 输入参数arxiv论文的ID,比如1812.10695",
467
+ "Function": HotReload(Latex翻译中文并重新编译PDF)
468
+ }
469
+ })
470
+ function_plugins.update({
471
+ "本地Latex论文精细翻译(上传Latex项目)[需Latex]": {
472
+ "Group": "学术",
473
+ "Color": "stop",
474
+ "AsButton": False,
475
+ "AdvancedArgs": True,
476
+ "ArgsReminder":
477
+ "如果有必要, 请在此处给出自定义翻译命令, 解决部分词汇翻译不准确的问题。 " +
478
+ "例如当单词'agent'翻译不准确时, 请尝试把以下指令复制到高级参数区: " +
479
+ 'If the term "agent" is used in this section, it should be translated to "智能体". ',
480
+ "Info": "本地Latex论文精细翻译 | 输入参数是路径",
481
+ "Function": HotReload(Latex翻译中文并重新编译PDF)
482
+ }
483
+ })
484
+ except:
485
+ print('Load function plugin failed')
486
+
487
+ try:
488
+ from toolbox import get_conf
489
+ ENABLE_AUDIO, = get_conf('ENABLE_AUDIO')
490
+ if ENABLE_AUDIO:
491
+ from crazy_functions.语音助手 import 语音助手
492
+ function_plugins.update({
493
+ "实时音频采集": {
494
+ "Group": "对话",
495
+ "Color": "stop",
496
+ "AsButton": True,
497
+ "Info": "开始语言对话 | 没有输入参数",
498
+ "Function": HotReload(语音助手)
499
+ }
500
+ })
501
+ except:
502
+ print('Load function plugin failed')
503
+
504
+ try:
505
+ from crazy_functions.批量翻译PDF文档_NOUGAT import 批量翻译PDF文档
506
+ function_plugins.update({
507
+ "精准翻译PDF文档(NOUGAT)": {
508
+ "Group": "学术",
509
+ "Color": "stop",
510
+ "AsButton": False,
511
+ "Function": HotReload(批量翻译PDF文档)
512
+ }
513
+ })
514
+ except:
515
+ print('Load function plugin failed')
516
+
517
+
518
+ # try:
519
+ # from crazy_functions.CodeInterpreter import 虚空终端CodeInterpreter
520
+ # function_plugins.update({
521
+ # "CodeInterpreter(开发中,仅供测试)": {
522
+ # "Group": "编程|对话",
523
+ # "Color": "stop",
524
+ # "AsButton": False,
525
+ # "Function": HotReload(虚空终端CodeInterpreter)
526
+ # }
527
+ # })
528
+ # except:
529
+ # print('Load function plugin failed')
530
+
531
+ # try:
532
+ # from crazy_functions.chatglm微调工具 import 微调数据集生成
533
+ # function_plugins.update({
534
+ # "黑盒模型学习: 微调数据集生成 (先上传数据集)": {
535
+ # "Color": "stop",
536
+ # "AsButton": False,
537
+ # "AdvancedArgs": True,
538
+ # "ArgsReminder": "针对数据集输入(如 绿帽子*深蓝色衬衫*黑色运动裤)给出指令,例如您可以将以下命令复制到下方: --llm_to_learn=azure-gpt-3.5 --prompt_prefix='根据下面的服装类型提示,想象一个穿着者,对这个人外貌、身处的环境、内心世界、过去经历进行描写。要求:100字以内,用第二人称。' --system_prompt=''",
539
+ # "Function": HotReload(微调数据集生成)
540
+ # }
541
+ # })
542
+ # except:
543
+ # print('Load function plugin failed')
544
+
545
+
546
+
547
+ """
548
+ 设置默认值:
549
+ - 默认 Group = 对话
550
+ - 默认 AsButton = True
551
+ - 默认 AdvancedArgs = False
552
+ - 默认 Color = secondary
553
+ """
554
+ for name, function_meta in function_plugins.items():
555
+ if "Group" not in function_meta:
556
+ function_plugins[name]["Group"] = '对话'
557
+ if "AsButton" not in function_meta:
558
+ function_plugins[name]["AsButton"] = True
559
+ if "AdvancedArgs" not in function_meta:
560
+ function_plugins[name]["AdvancedArgs"] = False
561
+ if "Color" not in function_meta:
562
+ function_plugins[name]["Color"] = 'secondary'
563
+
564
+ return function_plugins
crazy_functions/CodeInterpreter.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections.abc import Callable, Iterable, Mapping
2
+ from typing import Any
3
+ from toolbox import CatchException, update_ui, gen_time_str, trimmed_format_exc, promote_file_to_downloadzone, clear_file_downloadzone
4
+ from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive
5
+ from .crazy_utils import input_clipping, try_install_deps
6
+ from multiprocessing import Process, Pipe
7
+ import os
8
+ import time
9
+
10
+ templete = """
11
+ ```python
12
+ import ... # Put dependencies here, e.g. import numpy as np
13
+
14
+ class TerminalFunction(object): # Do not change the name of the class, The name of the class must be `TerminalFunction`
15
+
16
+ def run(self, path): # The name of the function must be `run`, it takes only a positional argument.
17
+ # rewrite the function you have just written here
18
+ ...
19
+ return generated_file_path
20
+ ```
21
+ """
22
+
23
+ def inspect_dependency(chatbot, history):
24
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
25
+ return True
26
+
27
+ def get_code_block(reply):
28
+ import re
29
+ pattern = r"```([\s\S]*?)```" # regex pattern to match code blocks
30
+ matches = re.findall(pattern, reply) # find all code blocks in text
31
+ if len(matches) == 1:
32
+ return matches[0].strip('python') # code block
33
+ for match in matches:
34
+ if 'class TerminalFunction' in match:
35
+ return match.strip('python') # code block
36
+ raise RuntimeError("GPT is not generating proper code.")
37
+
38
+ def gpt_interact_multi_step(txt, file_type, llm_kwargs, chatbot, history):
39
+ # 输入
40
+ prompt_compose = [
41
+ f'Your job:\n'
42
+ f'1. write a single Python function, which takes a path of a `{file_type}` file as the only argument and returns a `string` containing the result of analysis or the path of generated files. \n',
43
+ f"2. You should write this function to perform following task: " + txt + "\n",
44
+ f"3. Wrap the output python function with markdown codeblock."
45
+ ]
46
+ i_say = "".join(prompt_compose)
47
+ demo = []
48
+
49
+ # 第一步
50
+ gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
51
+ inputs=i_say, inputs_show_user=i_say,
52
+ llm_kwargs=llm_kwargs, chatbot=chatbot, history=demo,
53
+ sys_prompt= r"You are a programmer."
54
+ )
55
+ history.extend([i_say, gpt_say])
56
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新
57
+
58
+ # 第二步
59
+ prompt_compose = [
60
+ "If previous stage is successful, rewrite the function you have just written to satisfy following templete: \n",
61
+ templete
62
+ ]
63
+ i_say = "".join(prompt_compose); inputs_show_user = "If previous stage is successful, rewrite the function you have just written to satisfy executable templete. "
64
+ gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
65
+ inputs=i_say, inputs_show_user=inputs_show_user,
66
+ llm_kwargs=llm_kwargs, chatbot=chatbot, history=history,
67
+ sys_prompt= r"You are a programmer."
68
+ )
69
+ code_to_return = gpt_say
70
+ history.extend([i_say, gpt_say])
71
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新
72
+
73
+ # # 第三步
74
+ # i_say = "Please list to packages to install to run the code above. Then show me how to use `try_install_deps` function to install them."
75
+ # i_say += 'For instance. `try_install_deps(["opencv-python", "scipy", "numpy"])`'
76
+ # installation_advance = yield from request_gpt_model_in_new_thread_with_ui_alive(
77
+ # inputs=i_say, inputs_show_user=inputs_show_user,
78
+ # llm_kwargs=llm_kwargs, chatbot=chatbot, history=history,
79
+ # sys_prompt= r"You are a programmer."
80
+ # )
81
+ # # # 第三步
82
+ # i_say = "Show me how to use `pip` to install packages to run the code above. "
83
+ # i_say += 'For instance. `pip install -r opencv-python scipy numpy`'
84
+ # installation_advance = yield from request_gpt_model_in_new_thread_with_ui_alive(
85
+ # inputs=i_say, inputs_show_user=i_say,
86
+ # llm_kwargs=llm_kwargs, chatbot=chatbot, history=history,
87
+ # sys_prompt= r"You are a programmer."
88
+ # )
89
+ installation_advance = ""
90
+
91
+ return code_to_return, installation_advance, txt, file_type, llm_kwargs, chatbot, history
92
+
93
+ def make_module(code):
94
+ module_file = 'gpt_fn_' + gen_time_str().replace('-','_')
95
+ with open(f'gpt_log/{module_file}.py', 'w', encoding='utf8') as f:
96
+ f.write(code)
97
+
98
+ def get_class_name(class_string):
99
+ import re
100
+ # Use regex to extract the class name
101
+ class_name = re.search(r'class (\w+)\(', class_string).group(1)
102
+ return class_name
103
+
104
+ class_name = get_class_name(code)
105
+ return f"gpt_log.{module_file}->{class_name}"
106
+
107
+ def init_module_instance(module):
108
+ import importlib
109
+ module_, class_ = module.split('->')
110
+ init_f = getattr(importlib.import_module(module_), class_)
111
+ return init_f()
112
+
113
+ def for_immediate_show_off_when_possible(file_type, fp, chatbot):
114
+ if file_type in ['png', 'jpg']:
115
+ image_path = os.path.abspath(fp)
116
+ chatbot.append(['这是一张图片, 展示如下:',
117
+ f'本地文件地址: <br/>`{image_path}`<br/>'+
118
+ f'本地文件预览: <br/><div align="center"><img src="file={image_path}"></div>'
119
+ ])
120
+ return chatbot
121
+
122
+ def subprocess_worker(instance, file_path, return_dict):
123
+ return_dict['result'] = instance.run(file_path)
124
+
125
+ def have_any_recent_upload_files(chatbot):
126
+ _5min = 5 * 60
127
+ if not chatbot: return False # chatbot is None
128
+ most_recent_uploaded = chatbot._cookies.get("most_recent_uploaded", None)
129
+ if not most_recent_uploaded: return False # most_recent_uploaded is None
130
+ if time.time() - most_recent_uploaded["time"] < _5min: return True # most_recent_uploaded is new
131
+ else: return False # most_recent_uploaded is too old
132
+
133
+ def get_recent_file_prompt_support(chatbot):
134
+ most_recent_uploaded = chatbot._cookies.get("most_recent_uploaded", None)
135
+ path = most_recent_uploaded['path']
136
+ return path
137
+
138
+ @CatchException
139
+ def 虚空终端CodeInterpreter(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
140
+ """
141
+ txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
142
+ llm_kwargs gpt模型参数,如温度和top_p等,一般原样传递下去就行
143
+ plugin_kwargs 插件模型的参数,暂时没有用武之地
144
+ chatbot 聊天显示框的句柄,用于显示给用户
145
+ history 聊天历史,前情提要
146
+ system_prompt 给gpt的静默提醒
147
+ web_port 当前软件运行的端口号
148
+ """
149
+ raise NotImplementedError
150
+
151
+ # 清空历史,以免输入溢出
152
+ history = []; clear_file_downloadzone(chatbot)
153
+
154
+ # 基本信息:功能、贡献者
155
+ chatbot.append([
156
+ "函数插件功能?",
157
+ "CodeInterpreter开源版, 此插件处于开发阶段, 建议暂时不要使用, 插件初始化中 ..."
158
+ ])
159
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
160
+
161
+ if have_any_recent_upload_files(chatbot):
162
+ file_path = get_recent_file_prompt_support(chatbot)
163
+ else:
164
+ chatbot.append(["文件检索", "没有发现任何近期上传的文件。"])
165
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
166
+
167
+ # 读取文件
168
+ if ("recently_uploaded_files" in plugin_kwargs) and (plugin_kwargs["recently_uploaded_files"] == ""): plugin_kwargs.pop("recently_uploaded_files")
169
+ recently_uploaded_files = plugin_kwargs.get("recently_uploaded_files", None)
170
+ file_path = recently_uploaded_files[-1]
171
+ file_type = file_path.split('.')[-1]
172
+
173
+ # 粗心检查
174
+ if 'private_upload' in txt:
175
+ chatbot.append([
176
+ "...",
177
+ f"请在输入框内填写需求,然后再次点击该插件(文件路径 {file_path} 已经被记忆)"
178
+ ])
179
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
180
+ return
181
+
182
+ # 开始干正事
183
+ for j in range(5): # 最多重试5次
184
+ try:
185
+ code, installation_advance, txt, file_type, llm_kwargs, chatbot, history = \
186
+ yield from gpt_interact_multi_step(txt, file_type, llm_kwargs, chatbot, history)
187
+ code = get_code_block(code)
188
+ res = make_module(code)
189
+ instance = init_module_instance(res)
190
+ break
191
+ except Exception as e:
192
+ chatbot.append([f"第{j}次代码生成尝试,失败了", f"错误追踪\n```\n{trimmed_format_exc()}\n```\n"])
193
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
194
+
195
+ # 代码生成结束, 开始执行
196
+ try:
197
+ import multiprocessing
198
+ manager = multiprocessing.Manager()
199
+ return_dict = manager.dict()
200
+
201
+ p = multiprocessing.Process(target=subprocess_worker, args=(instance, file_path, return_dict))
202
+ # only has 10 seconds to run
203
+ p.start(); p.join(timeout=10)
204
+ if p.is_alive(): p.terminate(); p.join()
205
+ p.close()
206
+ res = return_dict['result']
207
+ # res = instance.run(file_path)
208
+ except Exception as e:
209
+ chatbot.append(["执行失败了", f"错误追踪\n```\n{trimmed_format_exc()}\n```\n"])
210
+ # chatbot.append(["如果是缺乏依赖,请参考以下建议", installation_advance])
211
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
212
+ return
213
+
214
+ # 顺利完成,收尾
215
+ res = str(res)
216
+ if os.path.exists(res):
217
+ chatbot.append(["执行成功了,结果是一个有效文件", "结果:" + res])
218
+ new_file_path = promote_file_to_downloadzone(res, chatbot=chatbot)
219
+ chatbot = for_immediate_show_off_when_possible(file_type, new_file_path, chatbot)
220
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新
221
+ else:
222
+ chatbot.append(["执行成功了,结果是一个字符串", "结果:" + res])
223
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 界面更新
224
+
225
+ """
226
+ 测试:
227
+ 裁剪图像,保留下半部分
228
+ 交换图像的蓝色通道和红色通道
229
+ 将图像转为灰度图像
230
+ 将csv文件转excel表格
231
+ """
crazy_functions/Langchain知识库.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import CatchException, update_ui, ProxyNetworkActivate
2
+ from .crazy_utils import request_gpt_model_in_new_thread_with_ui_alive, get_files_from_everything
3
+
4
+
5
+
6
+ @CatchException
7
+ def 知识库问答(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
8
+ """
9
+ txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
10
+ llm_kwargs gpt模型参数, 如温度和top_p等, 一般原样传递下去就行
11
+ plugin_kwargs 插件模型的参数,暂时没有用武之地
12
+ chatbot 聊天显示框的句柄,用于显示给用户
13
+ history 聊天历史,前情提要
14
+ system_prompt 给gpt的静默提醒
15
+ web_port 当前软件运行的端口号
16
+ """
17
+ history = [] # 清空历史,以免输入溢出
18
+ chatbot.append(("这是什么功能?", "[Local Message] 从一批文件(txt, md, tex)中读取数据构建知识库, 然后进行问答。"))
19
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
20
+
21
+ # resolve deps
22
+ try:
23
+ from zh_langchain import construct_vector_store
24
+ from langchain.embeddings.huggingface import HuggingFaceEmbeddings
25
+ from .crazy_utils import knowledge_archive_interface
26
+ except Exception as e:
27
+ chatbot.append(
28
+ ["依赖不足",
29
+ "导入依赖失败。正在尝试自动安装,请查看终端的输出或耐心等待..."]
30
+ )
31
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
32
+ from .crazy_utils import try_install_deps
33
+ try_install_deps(['zh_langchain==0.2.1', 'pypinyin'])
34
+
35
+ # < --------------------读取参数--------------- >
36
+ if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
37
+ kai_id = plugin_kwargs.get("advanced_arg", 'default')
38
+
39
+ # < --------------------读取文件--------------- >
40
+ file_manifest = []
41
+ spl = ["txt", "doc", "docx", "email", "epub", "html", "json", "md", "msg", "pdf", "ppt", "pptx", "rtf"]
42
+ for sp in spl:
43
+ _, file_manifest_tmp, _ = get_files_from_everything(txt, type=f'.{sp}')
44
+ file_manifest += file_manifest_tmp
45
+
46
+ if len(file_manifest) == 0:
47
+ chatbot.append(["没有找到任何可读取文件", "当前支持的格式包括: txt, md, docx, pptx, pdf, json等"])
48
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
49
+ return
50
+
51
+ # < -------------------预热文本向量化模组--------------- >
52
+ chatbot.append(['<br/>'.join(file_manifest), "正在预热文本向量化模组, 如果是第一次运行, 将消耗较长时间下载中文向量化模型..."])
53
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
54
+ print('Checking Text2vec ...')
55
+ from langchain.embeddings.huggingface import HuggingFaceEmbeddings
56
+ with ProxyNetworkActivate(): # 临时地激活代理网络
57
+ HuggingFaceEmbeddings(model_name="GanymedeNil/text2vec-large-chinese")
58
+
59
+ # < -------------------构建知识库--------------- >
60
+ chatbot.append(['<br/>'.join(file_manifest), "正在构建知识库..."])
61
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
62
+ print('Establishing knowledge archive ...')
63
+ with ProxyNetworkActivate(): # 临时地激活代理网络
64
+ kai = knowledge_archive_interface()
65
+ kai.feed_archive(file_manifest=file_manifest, id=kai_id)
66
+ kai_files = kai.get_loaded_file()
67
+ kai_files = '<br/>'.join(kai_files)
68
+ # chatbot.append(['知识库构建成功', "正在将知识库存储至cookie中"])
69
+ # yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
70
+ # chatbot._cookies['langchain_plugin_embedding'] = kai.get_current_archive_id()
71
+ # chatbot._cookies['lock_plugin'] = 'crazy_functions.Langchain知识库->读取知识库作答'
72
+ # chatbot.append(['完成', "“根据知识库作答”函数插件已经接管问答系统, 提问吧! 但注意, 您接下来不能再使用其他插件了,刷新页面即可以退出知识库问答模式。"])
73
+ chatbot.append(['构建完成', f"当前知识库内的有效文件:\n\n---\n\n{kai_files}\n\n---\n\n请切换至“知识库问答”插件进行知识库访问, 或者使用此插件继续上传更多文件。"])
74
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 由于请求gpt需要一段时间,我们先及时地做一次界面更新
75
+
76
+ @CatchException
77
+ def 读取知识库作答(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port=-1):
78
+ # resolve deps
79
+ try:
80
+ from zh_langchain import construct_vector_store
81
+ from langchain.embeddings.huggingface import HuggingFaceEmbeddings
82
+ from .crazy_utils import knowledge_archive_interface
83
+ except Exception as e:
84
+ chatbot.append(["依赖不足", "导入依赖失败。正在尝试自动安装,请查看终端的��出或耐心等待..."])
85
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
86
+ from .crazy_utils import try_install_deps
87
+ try_install_deps(['zh_langchain==0.2.1'])
88
+
89
+ # < ------------------- --------------- >
90
+ kai = knowledge_archive_interface()
91
+
92
+ if 'langchain_plugin_embedding' in chatbot._cookies:
93
+ resp, prompt = kai.answer_with_archive_by_id(txt, chatbot._cookies['langchain_plugin_embedding'])
94
+ else:
95
+ if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
96
+ kai_id = plugin_kwargs.get("advanced_arg", 'default')
97
+ resp, prompt = kai.answer_with_archive_by_id(txt, kai_id)
98
+
99
+ chatbot.append((txt, '[Local Message] ' + prompt))
100
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 由于请求gpt需要一段时间,我们先及时地做一次界面更新
101
+ gpt_say = yield from request_gpt_model_in_new_thread_with_ui_alive(
102
+ inputs=prompt, inputs_show_user=txt,
103
+ llm_kwargs=llm_kwargs, chatbot=chatbot, history=[],
104
+ sys_prompt=system_prompt
105
+ )
106
+ history.extend((prompt, gpt_say))
107
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面 # 由于请求gpt需要一段时间,我们先及时地做一次界面更新
crazy_functions/Latex全文润色.py ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import update_ui, trimmed_format_exc
2
+ from toolbox import CatchException, report_execption, write_results_to_file, zip_folder
3
+
4
+
5
+ class PaperFileGroup():
6
+ def __init__(self):
7
+ self.file_paths = []
8
+ self.file_contents = []
9
+ self.sp_file_contents = []
10
+ self.sp_file_index = []
11
+ self.sp_file_tag = []
12
+
13
+ # count_token
14
+ from request_llm.bridge_all import model_info
15
+ enc = model_info["gpt-3.5-turbo"]['tokenizer']
16
+ def get_token_num(txt): return len(enc.encode(txt, disallowed_special=()))
17
+ self.get_token_num = get_token_num
18
+
19
+ def run_file_split(self, max_token_limit=1900):
20
+ """
21
+ 将长文本分离开来
22
+ """
23
+ for index, file_content in enumerate(self.file_contents):
24
+ if self.get_token_num(file_content) < max_token_limit:
25
+ self.sp_file_contents.append(file_content)
26
+ self.sp_file_index.append(index)
27
+ self.sp_file_tag.append(self.file_paths[index])
28
+ else:
29
+ from .crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf
30
+ segments = breakdown_txt_to_satisfy_token_limit_for_pdf(file_content, self.get_token_num, max_token_limit)
31
+ for j, segment in enumerate(segments):
32
+ self.sp_file_contents.append(segment)
33
+ self.sp_file_index.append(index)
34
+ self.sp_file_tag.append(self.file_paths[index] + f".part-{j}.tex")
35
+
36
+ print('Segmentation: done')
37
+ def merge_result(self):
38
+ self.file_result = ["" for _ in range(len(self.file_paths))]
39
+ for r, k in zip(self.sp_file_result, self.sp_file_index):
40
+ self.file_result[k] += r
41
+
42
+ def write_result(self):
43
+ manifest = []
44
+ for path, res in zip(self.file_paths, self.file_result):
45
+ with open(path + '.polish.tex', 'w', encoding='utf8') as f:
46
+ manifest.append(path + '.polish.tex')
47
+ f.write(res)
48
+ return manifest
49
+
50
+ def zip_result(self):
51
+ import os, time
52
+ folder = os.path.dirname(self.file_paths[0])
53
+ t = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
54
+ zip_folder(folder, './gpt_log/', f'{t}-polished.zip')
55
+
56
+
57
+ def 多文件润色(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, language='en', mode='polish'):
58
+ import time, os, re
59
+ from .crazy_utils import request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency
60
+
61
+
62
+ # <-------- 读取Latex文件,删除其中的所有注释 ---------->
63
+ pfg = PaperFileGroup()
64
+
65
+ for index, fp in enumerate(file_manifest):
66
+ with open(fp, 'r', encoding='utf-8', errors='replace') as f:
67
+ file_content = f.read()
68
+ # 定义注释的正则表达式
69
+ comment_pattern = r'(?<!\\)%.*'
70
+ # 使用正则表达式查找注释,并替换为空字符串
71
+ clean_tex_content = re.sub(comment_pattern, '', file_content)
72
+ # 记录删除注释后的文本
73
+ pfg.file_paths.append(fp)
74
+ pfg.file_contents.append(clean_tex_content)
75
+
76
+ # <-------- 拆分过长的latex文件 ---------->
77
+ pfg.run_file_split(max_token_limit=1024)
78
+ n_split = len(pfg.sp_file_contents)
79
+
80
+
81
+ # <-------- 多线程润色开始 ---------->
82
+ if language == 'en':
83
+ if mode == 'polish':
84
+ inputs_array = ["Below is a section from an academic paper, polish this section to meet the academic standard, " +
85
+ "improve the grammar, clarity and overall readability, do not modify any latex command such as \section, \cite and equations:" +
86
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
87
+ else:
88
+ inputs_array = [r"Below is a section from an academic paper, proofread this section." +
89
+ r"Do not modify any latex command such as \section, \cite, \begin, \item and equations. " +
90
+ r"Answer me only with the revised text:" +
91
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
92
+ inputs_show_user_array = [f"Polish {f}" for f in pfg.sp_file_tag]
93
+ sys_prompt_array = ["You are a professional academic paper writer." for _ in range(n_split)]
94
+ elif language == 'zh':
95
+ if mode == 'polish':
96
+ inputs_array = [f"以下是一篇学术论文中的一段内容,请将此部分润色以满足学术标准,提高语法、清晰度和整体可读性,不要修改任何LaTeX命令,例如\section,\cite和方程式:" +
97
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
98
+ else:
99
+ inputs_array = [f"以下是一篇学术论文中的一段内容,请对这部分内容进行语法矫正。不要修改任何LaTeX命令,例如\section,\cite和方程式:" +
100
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
101
+ inputs_show_user_array = [f"润色 {f}" for f in pfg.sp_file_tag]
102
+ sys_prompt_array=["你是一位专业的中文学术论文作家。" for _ in range(n_split)]
103
+
104
+
105
+ gpt_response_collection = yield from request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
106
+ inputs_array=inputs_array,
107
+ inputs_show_user_array=inputs_show_user_array,
108
+ llm_kwargs=llm_kwargs,
109
+ chatbot=chatbot,
110
+ history_array=[[""] for _ in range(n_split)],
111
+ sys_prompt_array=sys_prompt_array,
112
+ # max_workers=5, # 并行任务数量限制,最多同时执行5个,其他的排队等待
113
+ scroller_max_len = 80
114
+ )
115
+
116
+ # <-------- 文本碎片重组为完整的tex文件,整理结果为压缩包 ---------->
117
+ try:
118
+ pfg.sp_file_result = []
119
+ for i_say, gpt_say in zip(gpt_response_collection[0::2], gpt_response_collection[1::2]):
120
+ pfg.sp_file_result.append(gpt_say)
121
+ pfg.merge_result()
122
+ pfg.write_result()
123
+ pfg.zip_result()
124
+ except:
125
+ print(trimmed_format_exc())
126
+
127
+ # <-------- 整理结果,退出 ---------->
128
+ create_report_file_name = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + f"-chatgpt.polish.md"
129
+ res = write_results_to_file(gpt_response_collection, file_name=create_report_file_name)
130
+ history = gpt_response_collection
131
+ chatbot.append((f"{fp}完成了吗?", res))
132
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
133
+
134
+
135
+ @CatchException
136
+ def Latex英文润色(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
137
+ # 基本信息:功能、贡献者
138
+ chatbot.append([
139
+ "函数插件功能?",
140
+ "对整个Latex项目进行润色。函数插件贡献者: Binary-Husky"])
141
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
142
+
143
+ # 尝试导入依赖,如果缺少依赖,则给出安装建议
144
+ try:
145
+ import tiktoken
146
+ except:
147
+ report_execption(chatbot, history,
148
+ a=f"解析项目: {txt}",
149
+ b=f"导入软件依赖失败。使用该模块需要额外依赖,安装方法```pip install --upgrade tiktoken```。")
150
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
151
+ return
152
+ history = [] # 清空历史,以免输入溢出
153
+ import glob, os
154
+ if os.path.exists(txt):
155
+ project_folder = txt
156
+ else:
157
+ if txt == "": txt = '空空如也的输入栏'
158
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无权访问: {txt}")
159
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
160
+ return
161
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)]
162
+ if len(file_manifest) == 0:
163
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}")
164
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
165
+ return
166
+ yield from 多文件润色(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, language='en')
167
+
168
+
169
+
170
+
171
+
172
+
173
+ @CatchException
174
+ def Latex中文润色(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
175
+ # 基本信息:功能、贡献者
176
+ chatbot.append([
177
+ "函数插件功能?",
178
+ "对整个Latex项目进行润色。函数插件贡献者: Binary-Husky"])
179
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
180
+
181
+ # 尝试导入依赖,如果缺少依赖,则给出安装建议
182
+ try:
183
+ import tiktoken
184
+ except:
185
+ report_execption(chatbot, history,
186
+ a=f"解析项目: {txt}",
187
+ b=f"导入软件依赖失败。使用该模块需要额外依赖,安装方法```pip install --upgrade tiktoken```。")
188
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
189
+ return
190
+ history = [] # 清空历史,以免输入溢出
191
+ import glob, os
192
+ if os.path.exists(txt):
193
+ project_folder = txt
194
+ else:
195
+ if txt == "": txt = '空空如也的输入栏'
196
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无权访问: {txt}")
197
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
198
+ return
199
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)]
200
+ if len(file_manifest) == 0:
201
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}")
202
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
203
+ return
204
+ yield from 多文件润色(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, language='zh')
205
+
206
+
207
+
208
+
209
+ @CatchException
210
+ def Latex英文纠错(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
211
+ # 基本信息:功能、贡献者
212
+ chatbot.append([
213
+ "函数插件功能?",
214
+ "对整个Latex项目进行纠错。函数插件贡献者: Binary-Husky"])
215
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
216
+
217
+ # 尝试导入依赖,如果缺少依赖,则给出安装建议
218
+ try:
219
+ import tiktoken
220
+ except:
221
+ report_execption(chatbot, history,
222
+ a=f"解析项目: {txt}",
223
+ b=f"导入软件依赖失败。使用该模块需要额外依赖,安装方法```pip install --upgrade tiktoken```。")
224
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
225
+ return
226
+ history = [] # 清空历史,以免输入溢出
227
+ import glob, os
228
+ if os.path.exists(txt):
229
+ project_folder = txt
230
+ else:
231
+ if txt == "": txt = '空空如也的输入栏'
232
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无权访问: {txt}")
233
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
234
+ return
235
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)]
236
+ if len(file_manifest) == 0:
237
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}")
238
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
239
+ return
240
+ yield from 多文件润色(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, language='en', mode='proofread')
241
+
242
+
243
+
crazy_functions/Latex全文翻译.py ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import update_ui
2
+ from toolbox import CatchException, report_execption, write_results_to_file
3
+ fast_debug = False
4
+
5
+ class PaperFileGroup():
6
+ def __init__(self):
7
+ self.file_paths = []
8
+ self.file_contents = []
9
+ self.sp_file_contents = []
10
+ self.sp_file_index = []
11
+ self.sp_file_tag = []
12
+
13
+ # count_token
14
+ from request_llm.bridge_all import model_info
15
+ enc = model_info["gpt-3.5-turbo"]['tokenizer']
16
+ def get_token_num(txt): return len(enc.encode(txt, disallowed_special=()))
17
+ self.get_token_num = get_token_num
18
+
19
+ def run_file_split(self, max_token_limit=1900):
20
+ """
21
+ 将长文本分离开来
22
+ """
23
+ for index, file_content in enumerate(self.file_contents):
24
+ if self.get_token_num(file_content) < max_token_limit:
25
+ self.sp_file_contents.append(file_content)
26
+ self.sp_file_index.append(index)
27
+ self.sp_file_tag.append(self.file_paths[index])
28
+ else:
29
+ from .crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf
30
+ segments = breakdown_txt_to_satisfy_token_limit_for_pdf(file_content, self.get_token_num, max_token_limit)
31
+ for j, segment in enumerate(segments):
32
+ self.sp_file_contents.append(segment)
33
+ self.sp_file_index.append(index)
34
+ self.sp_file_tag.append(self.file_paths[index] + f".part-{j}.tex")
35
+
36
+ print('Segmentation: done')
37
+
38
+ def 多文件翻译(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, language='en'):
39
+ import time, os, re
40
+ from .crazy_utils import request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency
41
+
42
+ # <-------- 读取Latex文件,删除其中的所有注释 ---------->
43
+ pfg = PaperFileGroup()
44
+
45
+ for index, fp in enumerate(file_manifest):
46
+ with open(fp, 'r', encoding='utf-8', errors='replace') as f:
47
+ file_content = f.read()
48
+ # 定义注释的正则表达式
49
+ comment_pattern = r'(?<!\\)%.*'
50
+ # 使用正则表达式查找注释,并替换为空字符串
51
+ clean_tex_content = re.sub(comment_pattern, '', file_content)
52
+ # 记录删除注释后的文本
53
+ pfg.file_paths.append(fp)
54
+ pfg.file_contents.append(clean_tex_content)
55
+
56
+ # <-------- 拆分过长的latex文件 ---------->
57
+ pfg.run_file_split(max_token_limit=1024)
58
+ n_split = len(pfg.sp_file_contents)
59
+
60
+ # <-------- 抽取摘要 ---------->
61
+ # if language == 'en':
62
+ # abs_extract_inputs = f"Please write an abstract for this paper"
63
+
64
+ # # 单线,获取文章meta信息
65
+ # paper_meta_info = yield from request_gpt_model_in_new_thread_with_ui_alive(
66
+ # inputs=abs_extract_inputs,
67
+ # inputs_show_user=f"正在抽取摘要信息。",
68
+ # llm_kwargs=llm_kwargs,
69
+ # chatbot=chatbot, history=[],
70
+ # sys_prompt="Your job is to collect information from materials。",
71
+ # )
72
+
73
+ # <-------- 多线程润色开始 ---------->
74
+ if language == 'en->zh':
75
+ inputs_array = ["Below is a section from an English academic paper, translate it into Chinese, do not modify any latex command such as \section, \cite and equations:" +
76
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
77
+ inputs_show_user_array = [f"翻译 {f}" for f in pfg.sp_file_tag]
78
+ sys_prompt_array = ["You are a professional academic paper translator." for _ in range(n_split)]
79
+ elif language == 'zh->en':
80
+ inputs_array = [f"Below is a section from a Chinese academic paper, translate it into English, do not modify any latex command such as \section, \cite and equations:" +
81
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
82
+ inputs_show_user_array = [f"翻译 {f}" for f in pfg.sp_file_tag]
83
+ sys_prompt_array = ["You are a professional academic paper translator." for _ in range(n_split)]
84
+
85
+ gpt_response_collection = yield from request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
86
+ inputs_array=inputs_array,
87
+ inputs_show_user_array=inputs_show_user_array,
88
+ llm_kwargs=llm_kwargs,
89
+ chatbot=chatbot,
90
+ history_array=[[""] for _ in range(n_split)],
91
+ sys_prompt_array=sys_prompt_array,
92
+ # max_workers=5, # OpenAI所允许的最大并行过载
93
+ scroller_max_len = 80
94
+ )
95
+
96
+ # <-------- 整理结果,退出 ---------->
97
+ create_report_file_name = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) + f"-chatgpt.polish.md"
98
+ res = write_results_to_file(gpt_response_collection, file_name=create_report_file_name)
99
+ history = gpt_response_collection
100
+ chatbot.append((f"{fp}完成了吗?", res))
101
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
102
+
103
+
104
+
105
+
106
+
107
+ @CatchException
108
+ def Latex英译中(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
109
+ # 基本信息:功能、贡献者
110
+ chatbot.append([
111
+ "函数插件功能?",
112
+ "对整个Latex项目进行翻译。函数插件贡献者: Binary-Husky"])
113
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
114
+
115
+ # 尝试导入依赖,如果缺少依赖,则给出安装建议
116
+ try:
117
+ import tiktoken
118
+ except:
119
+ report_execption(chatbot, history,
120
+ a=f"解析项目: {txt}",
121
+ b=f"导入软件依赖失败。使用该模块需要额外依赖,安装方法```pip install --upgrade tiktoken```。")
122
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
123
+ return
124
+ history = [] # 清空历史,以免输入溢出
125
+ import glob, os
126
+ if os.path.exists(txt):
127
+ project_folder = txt
128
+ else:
129
+ if txt == "": txt = '空空如也的输入栏'
130
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无权访问: {txt}")
131
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
132
+ return
133
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)]
134
+ if len(file_manifest) == 0:
135
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}")
136
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
137
+ return
138
+ yield from 多文件翻译(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, language='en->zh')
139
+
140
+
141
+
142
+
143
+
144
+ @CatchException
145
+ def Latex中译英(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
146
+ # 基本信息:功能、贡献者
147
+ chatbot.append([
148
+ "函数插件功能?",
149
+ "对整个Latex项目进行翻译。函数插件贡献者: Binary-Husky"])
150
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
151
+
152
+ # 尝试导入依赖,如果缺少依赖,则给出安装建议
153
+ try:
154
+ import tiktoken
155
+ except:
156
+ report_execption(chatbot, history,
157
+ a=f"解析项目: {txt}",
158
+ b=f"导入软件依赖失败。使用该模块需要额外依赖,安装方法```pip install --upgrade tiktoken```。")
159
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
160
+ return
161
+ history = [] # 清空历史,以免输入溢出
162
+ import glob, os
163
+ if os.path.exists(txt):
164
+ project_folder = txt
165
+ else:
166
+ if txt == "": txt = '空空如也的输入栏'
167
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无权访问: {txt}")
168
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
169
+ return
170
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)]
171
+ if len(file_manifest) == 0:
172
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}")
173
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
174
+ return
175
+ yield from 多文件翻译(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, language='zh->en')
crazy_functions/Latex输出PDF结果.py ADDED
@@ -0,0 +1,300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import update_ui, trimmed_format_exc, get_conf, objdump, objload, promote_file_to_downloadzone
2
+ from toolbox import CatchException, report_execption, update_ui_lastest_msg, zip_result, gen_time_str
3
+ from functools import partial
4
+ import glob, os, requests, time
5
+ pj = os.path.join
6
+ ARXIV_CACHE_DIR = os.path.expanduser(f"~/arxiv_cache/")
7
+
8
+ # =================================== 工具函数 ===============================================
9
+ # 专业词汇声明 = 'If the term "agent" is used in this section, it should be translated to "智能体". '
10
+ def switch_prompt(pfg, mode, more_requirement):
11
+ """
12
+ Generate prompts and system prompts based on the mode for proofreading or translating.
13
+ Args:
14
+ - pfg: Proofreader or Translator instance.
15
+ - mode: A string specifying the mode, either 'proofread' or 'translate_zh'.
16
+
17
+ Returns:
18
+ - inputs_array: A list of strings containing prompts for users to respond to.
19
+ - sys_prompt_array: A list of strings containing prompts for system prompts.
20
+ """
21
+ n_split = len(pfg.sp_file_contents)
22
+ if mode == 'proofread_en':
23
+ inputs_array = [r"Below is a section from an academic paper, proofread this section." +
24
+ r"Do not modify any latex command such as \section, \cite, \begin, \item and equations. " + more_requirement +
25
+ r"Answer me only with the revised text:" +
26
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
27
+ sys_prompt_array = ["You are a professional academic paper writer." for _ in range(n_split)]
28
+ elif mode == 'translate_zh':
29
+ inputs_array = [r"Below is a section from an English academic paper, translate it into Chinese. " + more_requirement +
30
+ r"Do not modify any latex command such as \section, \cite, \begin, \item and equations. " +
31
+ r"Answer me only with the translated text:" +
32
+ f"\n\n{frag}" for frag in pfg.sp_file_contents]
33
+ sys_prompt_array = ["You are a professional translator." for _ in range(n_split)]
34
+ else:
35
+ assert False, "未知指令"
36
+ return inputs_array, sys_prompt_array
37
+
38
+ def desend_to_extracted_folder_if_exist(project_folder):
39
+ """
40
+ Descend into the extracted folder if it exists, otherwise return the original folder.
41
+
42
+ Args:
43
+ - project_folder: A string specifying the folder path.
44
+
45
+ Returns:
46
+ - A string specifying the path to the extracted folder, or the original folder if there is no extracted folder.
47
+ """
48
+ maybe_dir = [f for f in glob.glob(f'{project_folder}/*') if os.path.isdir(f)]
49
+ if len(maybe_dir) == 0: return project_folder
50
+ if maybe_dir[0].endswith('.extract'): return maybe_dir[0]
51
+ return project_folder
52
+
53
+ def move_project(project_folder, arxiv_id=None):
54
+ """
55
+ Create a new work folder and copy the project folder to it.
56
+
57
+ Args:
58
+ - project_folder: A string specifying the folder path of the project.
59
+
60
+ Returns:
61
+ - A string specifying the path to the new work folder.
62
+ """
63
+ import shutil, time
64
+ time.sleep(2) # avoid time string conflict
65
+ if arxiv_id is not None:
66
+ new_workfolder = pj(ARXIV_CACHE_DIR, arxiv_id, 'workfolder')
67
+ else:
68
+ new_workfolder = f'gpt_log/{gen_time_str()}'
69
+ try:
70
+ shutil.rmtree(new_workfolder)
71
+ except:
72
+ pass
73
+
74
+ # align subfolder if there is a folder wrapper
75
+ items = glob.glob(pj(project_folder,'*'))
76
+ if len(glob.glob(pj(project_folder,'*.tex'))) == 0 and len(items) == 1:
77
+ if os.path.isdir(items[0]): project_folder = items[0]
78
+
79
+ shutil.copytree(src=project_folder, dst=new_workfolder)
80
+ return new_workfolder
81
+
82
+ def arxiv_download(chatbot, history, txt):
83
+ def check_cached_translation_pdf(arxiv_id):
84
+ translation_dir = pj(ARXIV_CACHE_DIR, arxiv_id, 'translation')
85
+ if not os.path.exists(translation_dir):
86
+ os.makedirs(translation_dir)
87
+ target_file = pj(translation_dir, 'translate_zh.pdf')
88
+ if os.path.exists(target_file):
89
+ promote_file_to_downloadzone(target_file, rename_file=None, chatbot=chatbot)
90
+ return target_file
91
+ return False
92
+ def is_float(s):
93
+ try:
94
+ float(s)
95
+ return True
96
+ except ValueError:
97
+ return False
98
+ if ('.' in txt) and ('/' not in txt) and is_float(txt): # is arxiv ID
99
+ txt = 'https://arxiv.org/abs/' + txt.strip()
100
+ if ('.' in txt) and ('/' not in txt) and is_float(txt[:10]): # is arxiv ID
101
+ txt = 'https://arxiv.org/abs/' + txt[:10]
102
+ if not txt.startswith('https://arxiv.org'):
103
+ return txt, None
104
+
105
+ # <-------------- inspect format ------------->
106
+ chatbot.append([f"检测到arxiv文档连接", '尝试下载 ...'])
107
+ yield from update_ui(chatbot=chatbot, history=history)
108
+ time.sleep(1) # 刷新界面
109
+
110
+ url_ = txt # https://arxiv.org/abs/1707.06690
111
+ if not txt.startswith('https://arxiv.org/abs/'):
112
+ msg = f"解析arxiv网���失败, 期望格式例如: https://arxiv.org/abs/1707.06690。实际得到格式: {url_}。"
113
+ yield from update_ui_lastest_msg(msg, chatbot=chatbot, history=history) # 刷新界面
114
+ return msg, None
115
+ # <-------------- set format ------------->
116
+ arxiv_id = url_.split('/abs/')[-1]
117
+ if 'v' in arxiv_id: arxiv_id = arxiv_id[:10]
118
+ cached_translation_pdf = check_cached_translation_pdf(arxiv_id)
119
+ if cached_translation_pdf: return cached_translation_pdf, arxiv_id
120
+
121
+ url_tar = url_.replace('/abs/', '/e-print/')
122
+ translation_dir = pj(ARXIV_CACHE_DIR, arxiv_id, 'e-print')
123
+ extract_dst = pj(ARXIV_CACHE_DIR, arxiv_id, 'extract')
124
+ os.makedirs(translation_dir, exist_ok=True)
125
+
126
+ # <-------------- download arxiv source file ------------->
127
+ dst = pj(translation_dir, arxiv_id+'.tar')
128
+ if os.path.exists(dst):
129
+ yield from update_ui_lastest_msg("调用缓存", chatbot=chatbot, history=history) # 刷新界面
130
+ else:
131
+ yield from update_ui_lastest_msg("开始下载", chatbot=chatbot, history=history) # 刷新界面
132
+ proxies, = get_conf('proxies')
133
+ r = requests.get(url_tar, proxies=proxies)
134
+ with open(dst, 'wb+') as f:
135
+ f.write(r.content)
136
+ # <-------------- extract file ------------->
137
+ yield from update_ui_lastest_msg("下载完成", chatbot=chatbot, history=history) # 刷新界面
138
+ from toolbox import extract_archive
139
+ extract_archive(file_path=dst, dest_dir=extract_dst)
140
+ return extract_dst, arxiv_id
141
+ # ========================================= 插件主程序1 =====================================================
142
+
143
+
144
+ @CatchException
145
+ def Latex英文纠错加PDF对比(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
146
+ # <-------------- information about this plugin ------------->
147
+ chatbot.append([ "函数插件功能?",
148
+ "对整个Latex项目进行纠错, 用latex编译为PDF对修正处做高亮。函数插件贡献者: Binary-Husky。注意事项: 目前仅支持GPT3.5/GPT4,其他模型转化效果未知。目前对机器学习类文献转化效果最好,其他类型文献转化效果未知。仅在Windows系统进行了测试,其他操作系统表现未知。"])
149
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
150
+
151
+ # <-------------- more requirements ------------->
152
+ if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
153
+ more_req = plugin_kwargs.get("advanced_arg", "")
154
+ _switch_prompt_ = partial(switch_prompt, more_requirement=more_req)
155
+
156
+ # <-------------- check deps ------------->
157
+ try:
158
+ import glob, os, time, subprocess
159
+ subprocess.Popen(['pdflatex', '-version'])
160
+ from .latex_fns.latex_actions import Latex精细分解与转化, 编译Latex
161
+ except Exception as e:
162
+ chatbot.append([ f"解析项目: {txt}",
163
+ f"尝试执行Latex指令失败。Latex没有安装, 或者不在环境变量PATH中。安装方法https://tug.org/texlive/。报错信息\n\n```\n\n{trimmed_format_exc()}\n\n```\n\n"])
164
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
165
+ return
166
+
167
+
168
+ # <-------------- clear history and read input ------------->
169
+ history = []
170
+ if os.path.exists(txt):
171
+ project_folder = txt
172
+ else:
173
+ if txt == "": txt = '空空如也的输入栏'
174
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无权访问: {txt}")
175
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
176
+ return
177
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)]
178
+ if len(file_manifest) == 0:
179
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}")
180
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
181
+ return
182
+
183
+
184
+ # <-------------- if is a zip/tar file ------------->
185
+ project_folder = desend_to_extracted_folder_if_exist(project_folder)
186
+
187
+
188
+ # <-------------- move latex project away from temp folder ------------->
189
+ project_folder = move_project(project_folder, arxiv_id=None)
190
+
191
+
192
+ # <-------------- if merge_translate_zh is already generated, skip gpt req ------------->
193
+ if not os.path.exists(project_folder + '/merge_proofread_en.tex'):
194
+ yield from Latex精细分解与转化(file_manifest, project_folder, llm_kwargs, plugin_kwargs,
195
+ chatbot, history, system_prompt, mode='proofread_en', switch_prompt=_switch_prompt_)
196
+
197
+
198
+ # <-------------- compile PDF ------------->
199
+ success = yield from 编译Latex(chatbot, history, main_file_original='merge', main_file_modified='merge_proofread_en',
200
+ work_folder_original=project_folder, work_folder_modified=project_folder, work_folder=project_folder)
201
+
202
+
203
+ # <-------------- zip PDF ------------->
204
+ zip_res = zip_result(project_folder)
205
+ if success:
206
+ chatbot.append((f"成功啦", '请查收结果(压缩包)...'))
207
+ yield from update_ui(chatbot=chatbot, history=history); time.sleep(1) # 刷新界面
208
+ promote_file_to_downloadzone(file=zip_res, chatbot=chatbot)
209
+ else:
210
+ chatbot.append((f"失败了", '虽然PDF生成失败了, 但请查收结果(压缩包), 内含已经翻译的Tex文档, 也是可读的, 您可以到Github Issue区, 用该压缩包+对话历史存档进行反馈 ...'))
211
+ yield from update_ui(chatbot=chatbot, history=history); time.sleep(1) # 刷新界面
212
+ promote_file_to_downloadzone(file=zip_res, chatbot=chatbot)
213
+
214
+ # <-------------- we are done ------------->
215
+ return success
216
+
217
+
218
+ # ========================================= 插件主程序2 =====================================================
219
+
220
+ @CatchException
221
+ def Latex翻译中文并重新编译PDF(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
222
+ # <-------------- information about this plugin ------------->
223
+ chatbot.append([
224
+ "函数插件功能?",
225
+ "对整个Latex项目进行翻译, 生成中文PDF。函数插件贡献者: Binary-Husky。注意事项: 此插件Windows支持最佳,Linux下必须使用Docker安装,详见项目主README.md。目前仅支持GPT3.5/GPT4,其他模型转化效果未知。目前对机器学习类文献转化效果最好,其他类型文献转化效果未知。"])
226
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
227
+
228
+ # <-------------- more requirements ------------->
229
+ if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
230
+ more_req = plugin_kwargs.get("advanced_arg", "")
231
+ _switch_prompt_ = partial(switch_prompt, more_requirement=more_req)
232
+
233
+ # <-------------- check deps ------------->
234
+ try:
235
+ import glob, os, time, subprocess
236
+ subprocess.Popen(['pdflatex', '-version'])
237
+ from .latex_fns.latex_actions import Latex精细分解与转化, 编译Latex
238
+ except Exception as e:
239
+ chatbot.append([ f"解析项目: {txt}",
240
+ f"尝试执行Latex指令失败。Latex没有安装, 或者不在环境变量PATH中。安装方法https://tug.org/texlive/。报错信息\n\n```\n\n{trimmed_format_exc()}\n\n```\n\n"])
241
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
242
+ return
243
+
244
+
245
+ # <-------------- clear history and read input ------------->
246
+ history = []
247
+ txt, arxiv_id = yield from arxiv_download(chatbot, history, txt)
248
+ if txt.endswith('.pdf'):
249
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"发现已经存在翻译好的PDF文档")
250
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
251
+ return
252
+
253
+
254
+ if os.path.exists(txt):
255
+ project_folder = txt
256
+ else:
257
+ if txt == "": txt = '空空如也的输入栏'
258
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到本地项目或无法处理: {txt}")
259
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
260
+ return
261
+
262
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*.tex', recursive=True)]
263
+ if len(file_manifest) == 0:
264
+ report_execption(chatbot, history, a = f"解析项目: {txt}", b = f"找不到任何.tex文件: {txt}")
265
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
266
+ return
267
+
268
+
269
+ # <-------------- if is a zip/tar file ------------->
270
+ project_folder = desend_to_extracted_folder_if_exist(project_folder)
271
+
272
+
273
+ # <-------------- move latex project away from temp folder ------------->
274
+ project_folder = move_project(project_folder, arxiv_id)
275
+
276
+
277
+ # <-------------- if merge_translate_zh is already generated, skip gpt req ------------->
278
+ if not os.path.exists(project_folder + '/merge_translate_zh.tex'):
279
+ yield from Latex精细分解与转化(file_manifest, project_folder, llm_kwargs, plugin_kwargs,
280
+ chatbot, history, system_prompt, mode='translate_zh', switch_prompt=_switch_prompt_)
281
+
282
+
283
+ # <-------------- compile PDF ------------->
284
+ success = yield from 编译Latex(chatbot, history, main_file_original='merge', main_file_modified='merge_translate_zh', mode='translate_zh',
285
+ work_folder_original=project_folder, work_folder_modified=project_folder, work_folder=project_folder)
286
+
287
+ # <-------------- zip PDF ------------->
288
+ zip_res = zip_result(project_folder)
289
+ if success:
290
+ chatbot.append((f"成功啦", '请查收结果(压缩包)...'))
291
+ yield from update_ui(chatbot=chatbot, history=history); time.sleep(1) # 刷新界面
292
+ promote_file_to_downloadzone(file=zip_res, chatbot=chatbot)
293
+ else:
294
+ chatbot.append((f"失败了", '虽然PDF生成失败了, 但请查收结果(压缩包), 内含已经翻译的Tex文档, 您可以到Github Issue区, 用该压缩包进行反馈。如系统是Linux,请检查系统字体(见Github wiki) ...'))
295
+ yield from update_ui(chatbot=chatbot, history=history); time.sleep(1) # 刷新界面
296
+ promote_file_to_downloadzone(file=zip_res, chatbot=chatbot)
297
+
298
+
299
+ # <-------------- we are done ------------->
300
+ return success
crazy_functions/__init__.py ADDED
File without changes
crazy_functions/chatglm微调工具.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import CatchException, update_ui, promote_file_to_downloadzone
2
+ from .crazy_utils import request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency
3
+ import datetime, json
4
+
5
+ def fetch_items(list_of_items, batch_size):
6
+ for i in range(0, len(list_of_items), batch_size):
7
+ yield list_of_items[i:i + batch_size]
8
+
9
+ def string_to_options(arguments):
10
+ import argparse
11
+ import shlex
12
+
13
+ # Create an argparse.ArgumentParser instance
14
+ parser = argparse.ArgumentParser()
15
+
16
+ # Add command-line arguments
17
+ parser.add_argument("--llm_to_learn", type=str, help="LLM model to learn", default="gpt-3.5-turbo")
18
+ parser.add_argument("--prompt_prefix", type=str, help="Prompt prefix", default='')
19
+ parser.add_argument("--system_prompt", type=str, help="System prompt", default='')
20
+ parser.add_argument("--batch", type=int, help="System prompt", default=50)
21
+ parser.add_argument("--pre_seq_len", type=int, help="pre_seq_len", default=50)
22
+ parser.add_argument("--learning_rate", type=float, help="learning_rate", default=2e-2)
23
+ parser.add_argument("--num_gpus", type=int, help="num_gpus", default=1)
24
+ parser.add_argument("--json_dataset", type=str, help="json_dataset", default="")
25
+ parser.add_argument("--ptuning_directory", type=str, help="ptuning_directory", default="")
26
+
27
+
28
+
29
+ # Parse the arguments
30
+ args = parser.parse_args(shlex.split(arguments))
31
+
32
+ return args
33
+
34
+ @CatchException
35
+ def 微调数据集生成(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
36
+ """
37
+ txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
38
+ llm_kwargs gpt模型参数,如温度和top_p等,一般原样传递下去就行
39
+ plugin_kwargs 插件模型的参数
40
+ chatbot 聊天显示框的句柄,用于显示给用户
41
+ history 聊天历史,前情提要
42
+ system_prompt 给gpt的静默提醒
43
+ web_port 当前软件运行的端口号
44
+ """
45
+ history = [] # 清空历史,以免输入溢出
46
+ chatbot.append(("这是什么功能?", "[Local Message] 微调数据集生成"))
47
+ if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
48
+ args = plugin_kwargs.get("advanced_arg", None)
49
+ if args is None:
50
+ chatbot.append(("没给定指令", "退出"))
51
+ yield from update_ui(chatbot=chatbot, history=history); return
52
+ else:
53
+ arguments = string_to_options(arguments=args)
54
+
55
+ dat = []
56
+ with open(txt, 'r', encoding='utf8') as f:
57
+ for line in f.readlines():
58
+ json_dat = json.loads(line)
59
+ dat.append(json_dat["content"])
60
+
61
+ llm_kwargs['llm_model'] = arguments.llm_to_learn
62
+ for batch in fetch_items(dat, arguments.batch):
63
+ res = yield from request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
64
+ inputs_array=[f"{arguments.prompt_prefix}\n\n{b}" for b in (batch)],
65
+ inputs_show_user_array=[f"Show Nothing" for _ in (batch)],
66
+ llm_kwargs=llm_kwargs,
67
+ chatbot=chatbot,
68
+ history_array=[[] for _ in (batch)],
69
+ sys_prompt_array=[arguments.system_prompt for _ in (batch)],
70
+ max_workers=10 # OpenAI所允许的最大并行过载
71
+ )
72
+
73
+ with open(txt+'.generated.json', 'a+', encoding='utf8') as f:
74
+ for b, r in zip(batch, res[1::2]):
75
+ f.write(json.dumps({"content":b, "summary":r}, ensure_ascii=False)+'\n')
76
+
77
+ promote_file_to_downloadzone(txt+'.generated.json', rename_file='generated.json', chatbot=chatbot)
78
+ return
79
+
80
+
81
+
82
+ @CatchException
83
+ def 启动微调(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
84
+ """
85
+ txt 输入栏用户输入的文本,例如需要翻译的一段话,再例如一个包含了待处理文件的路径
86
+ llm_kwargs gpt模型参数,如温度和top_p等,一般原样传递下去就行
87
+ plugin_kwargs 插件模型的参数
88
+ chatbot 聊天显示框的句柄,用于显示给用户
89
+ history 聊天历史,前情提要
90
+ system_prompt 给gpt的静默提醒
91
+ web_port 当前软件运行的端口号
92
+ """
93
+ import subprocess
94
+ history = [] # 清空历史,以免输入溢出
95
+ chatbot.append(("这是什么功能?", "[Local Message] 微调数据集生成"))
96
+ if ("advanced_arg" in plugin_kwargs) and (plugin_kwargs["advanced_arg"] == ""): plugin_kwargs.pop("advanced_arg")
97
+ args = plugin_kwargs.get("advanced_arg", None)
98
+ if args is None:
99
+ chatbot.append(("没给定指令", "退出"))
100
+ yield from update_ui(chatbot=chatbot, history=history); return
101
+ else:
102
+ arguments = string_to_options(arguments=args)
103
+
104
+
105
+
106
+ pre_seq_len = arguments.pre_seq_len # 128
107
+ learning_rate = arguments.learning_rate # 2e-2
108
+ num_gpus = arguments.num_gpus # 1
109
+ json_dataset = arguments.json_dataset # 't_code.json'
110
+ ptuning_directory = arguments.ptuning_directory # '/home/hmp/ChatGLM2-6B/ptuning'
111
+
112
+ command = f"torchrun --standalone --nnodes=1 --nproc-per-node={num_gpus} main.py \
113
+ --do_train \
114
+ --train_file AdvertiseGen/{json_dataset} \
115
+ --validation_file AdvertiseGen/{json_dataset} \
116
+ --preprocessing_num_workers 20 \
117
+ --prompt_column content \
118
+ --response_column summary \
119
+ --overwrite_cache \
120
+ --model_name_or_path THUDM/chatglm2-6b \
121
+ --output_dir output/clothgen-chatglm2-6b-pt-{pre_seq_len}-{learning_rate} \
122
+ --overwrite_output_dir \
123
+ --max_source_length 256 \
124
+ --max_target_length 256 \
125
+ --per_device_train_batch_size 1 \
126
+ --per_device_eval_batch_size 1 \
127
+ --gradient_accumulation_steps 16 \
128
+ --predict_with_generate \
129
+ --max_steps 100 \
130
+ --logging_steps 10 \
131
+ --save_steps 20 \
132
+ --learning_rate {learning_rate} \
133
+ --pre_seq_len {pre_seq_len} \
134
+ --quantization_bit 4"
135
+
136
+ process = subprocess.Popen(command, shell=True, cwd=ptuning_directory)
137
+ try:
138
+ process.communicate(timeout=3600*24)
139
+ except subprocess.TimeoutExpired:
140
+ process.kill()
141
+ return
crazy_functions/crazy_functions_test.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ 这是什么?
3
+ 这个文件用于函数插件的单元测试
4
+ 运行方法 python crazy_functions/crazy_functions_test.py
5
+ """
6
+
7
+ # ==============================================================================================================================
8
+
9
+ def validate_path():
10
+ import os, sys
11
+ dir_name = os.path.dirname(__file__)
12
+ root_dir_assume = os.path.abspath(os.path.dirname(__file__) + '/..')
13
+ os.chdir(root_dir_assume)
14
+ sys.path.append(root_dir_assume)
15
+ validate_path() # validate path so you can run from base directory
16
+
17
+ # ==============================================================================================================================
18
+
19
+ from colorful import *
20
+ from toolbox import get_conf, ChatBotWithCookies
21
+ import contextlib
22
+ import os
23
+ import sys
24
+ from functools import wraps
25
+ proxies, WEB_PORT, LLM_MODEL, CONCURRENT_COUNT, AUTHENTICATION, CHATBOT_HEIGHT, LAYOUT, API_KEY = \
26
+ get_conf('proxies', 'WEB_PORT', 'LLM_MODEL', 'CONCURRENT_COUNT', 'AUTHENTICATION', 'CHATBOT_HEIGHT', 'LAYOUT', 'API_KEY')
27
+
28
+ llm_kwargs = {
29
+ 'api_key': API_KEY,
30
+ 'llm_model': LLM_MODEL,
31
+ 'top_p':1.0,
32
+ 'max_length': None,
33
+ 'temperature':1.0,
34
+ }
35
+ plugin_kwargs = { }
36
+ chatbot = ChatBotWithCookies(llm_kwargs)
37
+ history = []
38
+ system_prompt = "Serve me as a writing and programming assistant."
39
+ web_port = 1024
40
+
41
+ # ==============================================================================================================================
42
+
43
+ def silence_stdout(func):
44
+ @wraps(func)
45
+ def wrapper(*args, **kwargs):
46
+ _original_stdout = sys.stdout
47
+ sys.stdout = open(os.devnull, 'w')
48
+ for q in func(*args, **kwargs):
49
+ sys.stdout = _original_stdout
50
+ yield q
51
+ sys.stdout = open(os.devnull, 'w')
52
+ sys.stdout.close()
53
+ sys.stdout = _original_stdout
54
+ return wrapper
55
+
56
+ class CLI_Printer():
57
+ def __init__(self) -> None:
58
+ self.pre_buf = ""
59
+
60
+ def print(self, buf):
61
+ bufp = ""
62
+ for index, chat in enumerate(buf):
63
+ a, b = chat
64
+ bufp += sprint亮靛('[Me]:' + a) + '\n'
65
+ bufp += '[GPT]:' + b
66
+ if index < len(buf)-1:
67
+ bufp += '\n'
68
+
69
+ if self.pre_buf!="" and bufp.startswith(self.pre_buf):
70
+ print(bufp[len(self.pre_buf):], end='')
71
+ else:
72
+ print('\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'+bufp, end='')
73
+ self.pre_buf = bufp
74
+ return
75
+
76
+ cli_printer = CLI_Printer()
77
+ # ==============================================================================================================================
78
+ def test_解析一个Python项目():
79
+ from crazy_functions.解析项目源代码 import 解析一个Python项目
80
+ txt = "crazy_functions/test_project/python/dqn"
81
+ for cookies, cb, hist, msg in 解析一个Python项目(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
82
+ print(cb)
83
+
84
+ def test_解析一个Cpp项目():
85
+ from crazy_functions.解析项目源代码 import 解析一个C项目
86
+ txt = "crazy_functions/test_project/cpp/cppipc"
87
+ for cookies, cb, hist, msg in 解析一个C项目(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
88
+ print(cb)
89
+
90
+ def test_Latex英文润色():
91
+ from crazy_functions.Latex全文润色 import Latex英文润色
92
+ txt = "crazy_functions/test_project/latex/attention"
93
+ for cookies, cb, hist, msg in Latex英文润色(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
94
+ print(cb)
95
+
96
+ def test_Markdown中译英():
97
+ from crazy_functions.批量Markdown翻译 import Markdown中译英
98
+ txt = "README.md"
99
+ for cookies, cb, hist, msg in Markdown中译英(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
100
+ print(cb)
101
+
102
+ def test_批量翻译PDF文档():
103
+ from crazy_functions.批量翻译PDF文档_多线程 import 批量翻译PDF文档
104
+ txt = "crazy_functions/test_project/pdf_and_word"
105
+ for cookies, cb, hist, msg in 批量翻译PDF文档(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
106
+ print(cb)
107
+
108
+ def test_谷歌检索小助手():
109
+ from crazy_functions.谷歌检索小助手 import 谷歌检索小助手
110
+ txt = "https://scholar.google.com/scholar?hl=en&as_sdt=0%2C5&q=auto+reinforcement+learning&btnG="
111
+ for cookies, cb, hist, msg in 谷歌检索小助手(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
112
+ print(cb)
113
+
114
+ def test_总结word文档():
115
+ from crazy_functions.总结word文档 import 总结word文档
116
+ txt = "crazy_functions/test_project/pdf_and_word"
117
+ for cookies, cb, hist, msg in 总结word文档(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
118
+ print(cb)
119
+
120
+ def test_下载arxiv论文并翻译摘要():
121
+ from crazy_functions.下载arxiv论文翻译摘要 import 下载arxiv论文并翻译摘要
122
+ txt = "1812.10695"
123
+ for cookies, cb, hist, msg in 下载arxiv论文并翻译摘要(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
124
+ print(cb)
125
+
126
+ def test_联网回答问题():
127
+ from crazy_functions.联网的ChatGPT import 连接网络回答问题
128
+ # txt = "谁是应急食品?"
129
+ # >> '根据以上搜索结果可以得知,应急食品是“原神”游戏中的角色派蒙的外号。'
130
+ # txt = "道路千万条,安全第一条。后面两句是?"
131
+ # >> '行车不规范,亲人两行泪。'
132
+ # txt = "You should have gone for the head. What does that mean?"
133
+ # >> The phrase "You should have gone for the head" is a quote from the Marvel movies, Avengers: Infinity War and Avengers: Endgame. It was spoken by the character Thanos in Infinity War and by Thor in Endgame.
134
+ txt = "AutoGPT是什么?"
135
+ for cookies, cb, hist, msg in 连接网络回答问题(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
136
+ print("当前问答:", cb[-1][-1].replace("\n"," "))
137
+ for i, it in enumerate(cb): print亮蓝(it[0]); print亮黄(it[1])
138
+
139
+ def test_解析ipynb文件():
140
+ from crazy_functions.解析JupyterNotebook import 解析ipynb文件
141
+ txt = "crazy_functions/test_samples"
142
+ for cookies, cb, hist, msg in 解析ipynb文件(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
143
+ print(cb)
144
+
145
+
146
+ def test_数学动画生成manim():
147
+ from crazy_functions.数学动画生成manim import 动画生成
148
+ txt = "A ball split into 2, and then split into 4, and finally split into 8."
149
+ for cookies, cb, hist, msg in 动画生成(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
150
+ print(cb)
151
+
152
+
153
+
154
+ def test_Markdown多语言():
155
+ from crazy_functions.批量Markdown翻译 import Markdown翻译指定语言
156
+ txt = "README.md"
157
+ history = []
158
+ for lang in ["English", "French", "Japanese", "Korean", "Russian", "Italian", "German", "Portuguese", "Arabic"]:
159
+ plugin_kwargs = {"advanced_arg": lang}
160
+ for cookies, cb, hist, msg in Markdown翻译指定语言(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
161
+ print(cb)
162
+
163
+ def test_Langchain知识库():
164
+ from crazy_functions.Langchain知识库 import 知识库问答
165
+ txt = "./"
166
+ chatbot = ChatBotWithCookies(llm_kwargs)
167
+ for cookies, cb, hist, msg in silence_stdout(知识库问答)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
168
+ cli_printer.print(cb) # print(cb)
169
+
170
+ chatbot = ChatBotWithCookies(cookies)
171
+ from crazy_functions.Langchain知识库 import 读取知识库作答
172
+ txt = "What is the installation method?"
173
+ for cookies, cb, hist, msg in silence_stdout(读取知识库作答)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
174
+ cli_printer.print(cb) # print(cb)
175
+
176
+ def test_Langchain知识库读取():
177
+ from crazy_functions.Langchain知识库 import 读取知识库作答
178
+ txt = "远程云服务器部署?"
179
+ for cookies, cb, hist, msg in silence_stdout(读取知识库作答)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
180
+ cli_printer.print(cb) # print(cb)
181
+
182
+ def test_Latex():
183
+ from crazy_functions.Latex输出PDF结果 import Latex英文纠错加PDF对比, Latex翻译中文并重新编译PDF
184
+
185
+ # txt = r"https://arxiv.org/abs/1706.03762"
186
+ # txt = r"https://arxiv.org/abs/1902.03185"
187
+ # txt = r"https://arxiv.org/abs/2305.18290"
188
+ # txt = r"https://arxiv.org/abs/2305.17608"
189
+ # txt = r"https://arxiv.org/abs/2211.16068" # ACE
190
+ # txt = r"C:\Users\x\arxiv_cache\2211.16068\workfolder" # ACE
191
+ # txt = r"https://arxiv.org/abs/2002.09253"
192
+ # txt = r"https://arxiv.org/abs/2306.07831"
193
+ # txt = r"https://arxiv.org/abs/2212.10156"
194
+ # txt = r"https://arxiv.org/abs/2211.11559"
195
+ # txt = r"https://arxiv.org/abs/2303.08774"
196
+ txt = r"https://arxiv.org/abs/2303.12712"
197
+ # txt = r"C:\Users\fuqingxu\arxiv_cache\2303.12712\workfolder"
198
+
199
+
200
+ for cookies, cb, hist, msg in (Latex翻译中文并重新编译PDF)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
201
+ cli_printer.print(cb) # print(cb)
202
+
203
+
204
+
205
+ # txt = "2302.02948.tar"
206
+ # print(txt)
207
+ # main_tex, work_folder = Latex预处理(txt)
208
+ # print('main tex:', main_tex)
209
+ # res = 编译Latex(main_tex, work_folder)
210
+ # # for cookies, cb, hist, msg in silence_stdout(编译Latex)(txt, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, web_port):
211
+ # cli_printer.print(cb) # print(cb)
212
+
213
+
214
+
215
+ # test_解析一个Python项目()
216
+ # test_Latex英文润色()
217
+ # test_Markdown中译英()
218
+ # test_批量翻译PDF文档()
219
+ # test_谷歌检索小助手()
220
+ # test_总结word文档()
221
+ # test_下载arxiv论文并翻译摘要()
222
+ # test_解析一个Cpp项目()
223
+ # test_联网回答问题()
224
+ # test_解析ipynb文件()
225
+ # test_数学动画生成manim()
226
+ # test_Langchain知识库()
227
+ # test_Langchain知识库读取()
228
+ if __name__ == "__main__":
229
+ test_Latex()
230
+ input("程序完成,回车退出。")
231
+ print("退出。")
crazy_functions/crazy_utils.py ADDED
@@ -0,0 +1,758 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import update_ui, get_conf, trimmed_format_exc
2
+ import threading
3
+
4
+ def input_clipping(inputs, history, max_token_limit):
5
+ import numpy as np
6
+ from request_llm.bridge_all import model_info
7
+ enc = model_info["gpt-3.5-turbo"]['tokenizer']
8
+ def get_token_num(txt): return len(enc.encode(txt, disallowed_special=()))
9
+
10
+ mode = 'input-and-history'
11
+ # 当 输入部分的token占比 小于 全文的一半时,只裁剪历史
12
+ input_token_num = get_token_num(inputs)
13
+ if input_token_num < max_token_limit//2:
14
+ mode = 'only-history'
15
+ max_token_limit = max_token_limit - input_token_num
16
+
17
+ everything = [inputs] if mode == 'input-and-history' else ['']
18
+ everything.extend(history)
19
+ n_token = get_token_num('\n'.join(everything))
20
+ everything_token = [get_token_num(e) for e in everything]
21
+ delta = max(everything_token) // 16 # 截断时的颗粒度
22
+
23
+ while n_token > max_token_limit:
24
+ where = np.argmax(everything_token)
25
+ encoded = enc.encode(everything[where], disallowed_special=())
26
+ clipped_encoded = encoded[:len(encoded)-delta]
27
+ everything[where] = enc.decode(clipped_encoded)[:-1] # -1 to remove the may-be illegal char
28
+ everything_token[where] = get_token_num(everything[where])
29
+ n_token = get_token_num('\n'.join(everything))
30
+
31
+ if mode == 'input-and-history':
32
+ inputs = everything[0]
33
+ else:
34
+ pass
35
+ history = everything[1:]
36
+ return inputs, history
37
+
38
+ def request_gpt_model_in_new_thread_with_ui_alive(
39
+ inputs, inputs_show_user, llm_kwargs,
40
+ chatbot, history, sys_prompt, refresh_interval=0.2,
41
+ handle_token_exceed=True,
42
+ retry_times_at_unknown_error=2,
43
+ ):
44
+ """
45
+ Request GPT model,请求GPT模型同时维持用户界面活跃。
46
+
47
+ 输入参数 Args (以_array结尾的输入变量都是列表,列表长度为子任务的数量,执行时,会把列表拆解,放到每个子线程中分别执行):
48
+ inputs (string): List of inputs (输入)
49
+ inputs_show_user (string): List of inputs to show user(展现在报告中的输入,借助此参数,在汇总报告中隐藏啰嗦的真实输入,增强报告的可读性)
50
+ top_p (float): Top p value for sampling from model distribution (GPT参数,浮点数)
51
+ temperature (float): Temperature value for sampling from model distribution(GPT参数,浮点数)
52
+ chatbot: chatbot inputs and outputs (用户界面对话窗口句柄,用于数据流可视化)
53
+ history (list): List of chat history (历史,对话历史列表)
54
+ sys_prompt (string): List of system prompts (系统输入,列表,用于输入给GPT的前提提示,比如你是翻译官怎样怎样)
55
+ refresh_interval (float, optional): Refresh interval for UI (default: 0.2) (刷新时间间隔频率,建议低于1,不可高于3,仅仅服务于视觉效果)
56
+ handle_token_exceed:是否自动处理token溢出的情况,如果选择自动处理,则会在溢出时暴力截断,默认开启
57
+ retry_times_at_unknown_error:失败时的重试次数
58
+
59
+ 输出 Returns:
60
+ future: 输出,GPT返回的结果
61
+ """
62
+ import time
63
+ from concurrent.futures import ThreadPoolExecutor
64
+ from request_llm.bridge_all import predict_no_ui_long_connection
65
+ # 用户反馈
66
+ chatbot.append([inputs_show_user, ""])
67
+ yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面
68
+ executor = ThreadPoolExecutor(max_workers=16)
69
+ mutable = ["", time.time(), ""]
70
+ def _req_gpt(inputs, history, sys_prompt):
71
+ retry_op = retry_times_at_unknown_error
72
+ exceeded_cnt = 0
73
+ while True:
74
+ # watchdog error
75
+ if len(mutable) >= 2 and (time.time()-mutable[1]) > 5:
76
+ raise RuntimeError("检测到程序终止。")
77
+ try:
78
+ # 【第一种情况】:顺利完成
79
+ result = predict_no_ui_long_connection(
80
+ inputs=inputs, llm_kwargs=llm_kwargs,
81
+ history=history, sys_prompt=sys_prompt, observe_window=mutable)
82
+ return result
83
+ except ConnectionAbortedError as token_exceeded_error:
84
+ # 【第二种情况】:Token溢出
85
+ if handle_token_exceed:
86
+ exceeded_cnt += 1
87
+ # 【选择处理】 尝试计算比例,尽可能多地保留文本
88
+ from toolbox import get_reduce_token_percent
89
+ p_ratio, n_exceed = get_reduce_token_percent(str(token_exceeded_error))
90
+ MAX_TOKEN = 4096
91
+ EXCEED_ALLO = 512 + 512 * exceeded_cnt
92
+ inputs, history = input_clipping(inputs, history, max_token_limit=MAX_TOKEN-EXCEED_ALLO)
93
+ mutable[0] += f'[Local Message] 警告,文本过长将进行截断,Token溢出数:{n_exceed}。\n\n'
94
+ continue # 返回重试
95
+ else:
96
+ # 【选择放弃】
97
+ tb_str = '```\n' + trimmed_format_exc() + '```'
98
+ mutable[0] += f"[Local Message] 警告,在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n"
99
+ return mutable[0] # 放弃
100
+ except:
101
+ # 【第三种情况】:其他错误:重试几次
102
+ tb_str = '```\n' + trimmed_format_exc() + '```'
103
+ print(tb_str)
104
+ mutable[0] += f"[Local Message] 警告,在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n"
105
+ if retry_op > 0:
106
+ retry_op -= 1
107
+ mutable[0] += f"[Local Message] 重试中,请稍等 {retry_times_at_unknown_error-retry_op}/{retry_times_at_unknown_error}:\n\n"
108
+ if ("Rate limit reached" in tb_str) or ("Too Many Requests" in tb_str):
109
+ time.sleep(30)
110
+ time.sleep(5)
111
+ continue # 返回重试
112
+ else:
113
+ time.sleep(5)
114
+ return mutable[0] # 放弃
115
+
116
+ # 提交任务
117
+ future = executor.submit(_req_gpt, inputs, history, sys_prompt)
118
+ while True:
119
+ # yield一次以刷新前端页面
120
+ time.sleep(refresh_interval)
121
+ # “喂狗”(看门狗)
122
+ mutable[1] = time.time()
123
+ if future.done():
124
+ break
125
+ chatbot[-1] = [chatbot[-1][0], mutable[0]]
126
+ yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面
127
+
128
+ final_result = future.result()
129
+ chatbot[-1] = [chatbot[-1][0], final_result]
130
+ yield from update_ui(chatbot=chatbot, history=[]) # 如果最后成功了,则删除报错信息
131
+ return final_result
132
+
133
+ def can_multi_process(llm):
134
+ if llm.startswith('gpt-'): return True
135
+ if llm.startswith('api2d-'): return True
136
+ if llm.startswith('azure-'): return True
137
+ return False
138
+
139
+ def request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
140
+ inputs_array, inputs_show_user_array, llm_kwargs,
141
+ chatbot, history_array, sys_prompt_array,
142
+ refresh_interval=0.2, max_workers=-1, scroller_max_len=30,
143
+ handle_token_exceed=True, show_user_at_complete=False,
144
+ retry_times_at_unknown_error=2,
145
+ ):
146
+ """
147
+ Request GPT model using multiple threads with UI and high efficiency
148
+ 请求GPT模型的[多线程]版。
149
+ 具备以下功能:
150
+ 实时在UI上反馈远程数据流
151
+ 使用线程池,可调节线程池的大小避免openai的流量限制错误
152
+ 处理中途中止的情况
153
+ 网络等出问题时,会把traceback和已经接收的数据转入输出
154
+
155
+ 输入参数 Args (以_array结尾的输入变量都是列表,列表长度为子任务的数量,执行时,会把列表拆解,放到每个子线程中分别执行):
156
+ inputs_array (list): List of inputs (每个子任务的输入)
157
+ inputs_show_user_array (list): List of inputs to show user(每个子任务展现在报告中的输入,借助此参数,在汇总报告中隐藏啰嗦的真实输入,增强报告的可读性)
158
+ llm_kwargs: llm_kwargs参数
159
+ chatbot: chatbot (用户界面对话窗口句柄,用于数据流可视化)
160
+ history_array (list): List of chat history (历史对话输入,双层列表,第一层列表是子任务分解,第二层列表是对话历史)
161
+ sys_prompt_array (list): List of system prompts (系统输入,列表,用于输入给GPT的前提提示,比如你是翻译官怎样怎样)
162
+ refresh_interval (float, optional): Refresh interval for UI (default: 0.2) (刷新时间间隔频率,建议低于1,不可高于3,仅仅服务于视觉效果)
163
+ max_workers (int, optional): Maximum number of threads (default: see config.py) (最大线程数,如果子任务非常多,需要用此选项防止高频地请求openai导致错误)
164
+ scroller_max_len (int, optional): Maximum length for scroller (default: 30)(数据流的显示最后收到的多少个字符,仅仅服务于视觉效果)
165
+ handle_token_exceed (bool, optional): (是否在输入过长时,自动缩减文本)
166
+ handle_token_exceed:是否自动处理token溢出的情况,如果选择自动处理,则会在溢出时暴力截断,默认开启
167
+ show_user_at_complete (bool, optional): (在结束时,把完整输入-输出结果显示在聊天框)
168
+ retry_times_at_unknown_error:子任务失败时的重试次数
169
+
170
+ 输出 Returns:
171
+ list: List of GPT model responses (每个子任务的输出汇总,如果某个子任务出错,response中会携带traceback报错信息,方便调试和定位问题。)
172
+ """
173
+ import time, random
174
+ from concurrent.futures import ThreadPoolExecutor
175
+ from request_llm.bridge_all import predict_no_ui_long_connection
176
+ assert len(inputs_array) == len(history_array)
177
+ assert len(inputs_array) == len(sys_prompt_array)
178
+ if max_workers == -1: # 读取配置文件
179
+ try: max_workers, = get_conf('DEFAULT_WORKER_NUM')
180
+ except: max_workers = 8
181
+ if max_workers <= 0: max_workers = 3
182
+ # 屏蔽掉 chatglm的多线程,可能会导致严重卡顿
183
+ if not can_multi_process(llm_kwargs['llm_model']):
184
+ max_workers = 1
185
+
186
+ executor = ThreadPoolExecutor(max_workers=max_workers)
187
+ n_frag = len(inputs_array)
188
+ # 用户反馈
189
+ chatbot.append(["请开始多线程操作。", ""])
190
+ yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面
191
+ # 跨线程传递
192
+ mutable = [["", time.time(), "等待中"] for _ in range(n_frag)]
193
+
194
+ # 子线程任务
195
+ def _req_gpt(index, inputs, history, sys_prompt):
196
+ gpt_say = ""
197
+ retry_op = retry_times_at_unknown_error
198
+ exceeded_cnt = 0
199
+ mutable[index][2] = "执行中"
200
+ while True:
201
+ # watchdog error
202
+ if len(mutable[index]) >= 2 and (time.time()-mutable[index][1]) > 5:
203
+ raise RuntimeError("检测到程序终止。")
204
+ try:
205
+ # 【第一种情况】:顺利完成
206
+ # time.sleep(10); raise RuntimeError("测试")
207
+ gpt_say = predict_no_ui_long_connection(
208
+ inputs=inputs, llm_kwargs=llm_kwargs, history=history,
209
+ sys_prompt=sys_prompt, observe_window=mutable[index], console_slience=True
210
+ )
211
+ mutable[index][2] = "已成功"
212
+ return gpt_say
213
+ except ConnectionAbortedError as token_exceeded_error:
214
+ # 【第二种情况】:Token溢出,
215
+ if handle_token_exceed:
216
+ exceeded_cnt += 1
217
+ # 【选择处理】 尝试计算比例,尽可能多地保留文本
218
+ from toolbox import get_reduce_token_percent
219
+ p_ratio, n_exceed = get_reduce_token_percent(str(token_exceeded_error))
220
+ MAX_TOKEN = 4096
221
+ EXCEED_ALLO = 512 + 512 * exceeded_cnt
222
+ inputs, history = input_clipping(inputs, history, max_token_limit=MAX_TOKEN-EXCEED_ALLO)
223
+ gpt_say += f'[Local Message] 警告,文本过长将进行截断,Token溢出数:{n_exceed}。\n\n'
224
+ mutable[index][2] = f"截断重试"
225
+ continue # 返回重试
226
+ else:
227
+ # 【选择放弃】
228
+ tb_str = '```\n' + trimmed_format_exc() + '```'
229
+ gpt_say += f"[Local Message] 警告,线程{index}在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n"
230
+ if len(mutable[index][0]) > 0: gpt_say += "此线程失败前收到的回答:\n\n" + mutable[index][0]
231
+ mutable[index][2] = "输入过长已放弃"
232
+ return gpt_say # 放弃
233
+ except:
234
+ # 【第三种情况】:其他错误
235
+ tb_str = '```\n' + trimmed_format_exc() + '```'
236
+ print(tb_str)
237
+ gpt_say += f"[Local Message] 警告,线程{index}在执行过程中遭遇问题, Traceback:\n\n{tb_str}\n\n"
238
+ if len(mutable[index][0]) > 0: gpt_say += "此线程失败前收到的回答:\n\n" + mutable[index][0]
239
+ if retry_op > 0:
240
+ retry_op -= 1
241
+ wait = random.randint(5, 20)
242
+ if ("Rate limit reached" in tb_str) or ("Too Many Requests" in tb_str):
243
+ wait = wait * 3
244
+ fail_info = "OpenAI绑定信用卡可解除频率限制 "
245
+ else:
246
+ fail_info = ""
247
+ # 也许等待十几秒后,情况会好转
248
+ for i in range(wait):
249
+ mutable[index][2] = f"{fail_info}等待重试 {wait-i}"; time.sleep(1)
250
+ # 开始重试
251
+ mutable[index][2] = f"重试中 {retry_times_at_unknown_error-retry_op}/{retry_times_at_unknown_error}"
252
+ continue # 返回重试
253
+ else:
254
+ mutable[index][2] = "已失败"
255
+ wait = 5
256
+ time.sleep(5)
257
+ return gpt_say # 放弃
258
+
259
+ # 异步任务开始
260
+ futures = [executor.submit(_req_gpt, index, inputs, history, sys_prompt) for index, inputs, history, sys_prompt in zip(
261
+ range(len(inputs_array)), inputs_array, history_array, sys_prompt_array)]
262
+ cnt = 0
263
+ while True:
264
+ # yield一次以刷新前端页面
265
+ time.sleep(refresh_interval)
266
+ cnt += 1
267
+ worker_done = [h.done() for h in futures]
268
+ # 更好的UI视觉效果
269
+ observe_win = []
270
+ # 每个线程都要“喂狗”(看门狗)
271
+ for thread_index, _ in enumerate(worker_done):
272
+ mutable[thread_index][1] = time.time()
273
+ # 在前端打印些好玩的东西
274
+ for thread_index, _ in enumerate(worker_done):
275
+ print_something_really_funny = "[ ...`"+mutable[thread_index][0][-scroller_max_len:].\
276
+ replace('\n', '').replace('```', '...').replace(
277
+ ' ', '.').replace('<br/>', '.....').replace('$', '.')+"`... ]"
278
+ observe_win.append(print_something_really_funny)
279
+ # 在前端打印些好玩的东西
280
+ stat_str = ''.join([f'`{mutable[thread_index][2]}`: {obs}\n\n'
281
+ if not done else f'`{mutable[thread_index][2]}`\n\n'
282
+ for thread_index, done, obs in zip(range(len(worker_done)), worker_done, observe_win)])
283
+ # 在前端打印些好玩的东西
284
+ chatbot[-1] = [chatbot[-1][0], f'多线程操作已经开始,完成情况: \n\n{stat_str}' + ''.join(['.']*(cnt % 10+1))]
285
+ yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面
286
+ if all(worker_done):
287
+ executor.shutdown()
288
+ break
289
+
290
+ # 异步任务结束
291
+ gpt_response_collection = []
292
+ for inputs_show_user, f in zip(inputs_show_user_array, futures):
293
+ gpt_res = f.result()
294
+ gpt_response_collection.extend([inputs_show_user, gpt_res])
295
+
296
+ # 是否在结束时,在界面上显示结果
297
+ if show_user_at_complete:
298
+ for inputs_show_user, f in zip(inputs_show_user_array, futures):
299
+ gpt_res = f.result()
300
+ chatbot.append([inputs_show_user, gpt_res])
301
+ yield from update_ui(chatbot=chatbot, history=[]) # 刷新界面
302
+ time.sleep(0.3)
303
+ return gpt_response_collection
304
+
305
+
306
+ def breakdown_txt_to_satisfy_token_limit(txt, get_token_fn, limit):
307
+ def cut(txt_tocut, must_break_at_empty_line): # 递归
308
+ if get_token_fn(txt_tocut) <= limit:
309
+ return [txt_tocut]
310
+ else:
311
+ lines = txt_tocut.split('\n')
312
+ estimated_line_cut = limit / get_token_fn(txt_tocut) * len(lines)
313
+ estimated_line_cut = int(estimated_line_cut)
314
+ for cnt in reversed(range(estimated_line_cut)):
315
+ if must_break_at_empty_line:
316
+ if lines[cnt] != "":
317
+ continue
318
+ print(cnt)
319
+ prev = "\n".join(lines[:cnt])
320
+ post = "\n".join(lines[cnt:])
321
+ if get_token_fn(prev) < limit:
322
+ break
323
+ if cnt == 0:
324
+ raise RuntimeError("存在一行极长的文本!")
325
+ # print(len(post))
326
+ # 列表递归接龙
327
+ result = [prev]
328
+ result.extend(cut(post, must_break_at_empty_line))
329
+ return result
330
+ try:
331
+ return cut(txt, must_break_at_empty_line=True)
332
+ except RuntimeError:
333
+ return cut(txt, must_break_at_empty_line=False)
334
+
335
+
336
+ def force_breakdown(txt, limit, get_token_fn):
337
+ """
338
+ 当无法用标点、空行分割时,我们用最暴力的方法切割
339
+ """
340
+ for i in reversed(range(len(txt))):
341
+ if get_token_fn(txt[:i]) < limit:
342
+ return txt[:i], txt[i:]
343
+ return "Tiktoken未知错误", "Tiktoken未知错误"
344
+
345
+ def breakdown_txt_to_satisfy_token_limit_for_pdf(txt, get_token_fn, limit):
346
+ # 递归
347
+ def cut(txt_tocut, must_break_at_empty_line, break_anyway=False):
348
+ if get_token_fn(txt_tocut) <= limit:
349
+ return [txt_tocut]
350
+ else:
351
+ lines = txt_tocut.split('\n')
352
+ estimated_line_cut = limit / get_token_fn(txt_tocut) * len(lines)
353
+ estimated_line_cut = int(estimated_line_cut)
354
+ cnt = 0
355
+ for cnt in reversed(range(estimated_line_cut)):
356
+ if must_break_at_empty_line:
357
+ if lines[cnt] != "":
358
+ continue
359
+ prev = "\n".join(lines[:cnt])
360
+ post = "\n".join(lines[cnt:])
361
+ if get_token_fn(prev) < limit:
362
+ break
363
+ if cnt == 0:
364
+ if break_anyway:
365
+ prev, post = force_breakdown(txt_tocut, limit, get_token_fn)
366
+ else:
367
+ raise RuntimeError(f"存在一行极长的文本!{txt_tocut}")
368
+ # print(len(post))
369
+ # 列表递归接龙
370
+ result = [prev]
371
+ result.extend(cut(post, must_break_at_empty_line, break_anyway=break_anyway))
372
+ return result
373
+ try:
374
+ # 第1次尝试,将双空行(\n\n)作为切分点
375
+ return cut(txt, must_break_at_empty_line=True)
376
+ except RuntimeError:
377
+ try:
378
+ # 第2次尝试,将单空行(\n)作为切分点
379
+ return cut(txt, must_break_at_empty_line=False)
380
+ except RuntimeError:
381
+ try:
382
+ # 第3次尝试,将英文句号(.)作为切分点
383
+ res = cut(txt.replace('.', '。\n'), must_break_at_empty_line=False) # 这个中文的句号是故意的,作为一个标识而存在
384
+ return [r.replace('。\n', '.') for r in res]
385
+ except RuntimeError as e:
386
+ try:
387
+ # 第4次尝试,将中文句号(。)作为切分点
388
+ res = cut(txt.replace('。', '。。\n'), must_break_at_empty_line=False)
389
+ return [r.replace('。。\n', '。') for r in res]
390
+ except RuntimeError as e:
391
+ # 第5次尝试,没办法了,随便切一下敷衍吧
392
+ return cut(txt, must_break_at_empty_line=False, break_anyway=True)
393
+
394
+
395
+
396
+ def read_and_clean_pdf_text(fp):
397
+ """
398
+ 这个函数用于分割pdf,用了很多trick,逻辑较乱,效果奇好
399
+
400
+ **输入参数说明**
401
+ - `fp`:需要读取和清理文本的pdf文件路径
402
+
403
+ **输出参数说明**
404
+ - `meta_txt`:清理后的文本内容字符串
405
+ - `page_one_meta`:第一页清理后的文本内容列表
406
+
407
+ **函数功能**
408
+ 读取pdf文件并清理其中的文本内容,清理规则包括:
409
+ - 提取所有块元的文本信息,并合并为一个字符串
410
+ - 去除短块(字符数小于100)并替换为回车符
411
+ - 清理多余的空行
412
+ - 合并小写字母开头的段落块并替换为空格
413
+ - 清除重复的换行
414
+ - 将每个换行符替换为两个换行符,使每个段落之间有两个换行符分隔
415
+ """
416
+ import fitz, copy
417
+ import re
418
+ import numpy as np
419
+ from colorful import print亮黄, print亮绿
420
+ fc = 0 # Index 0 文本
421
+ fs = 1 # Index 1 字体
422
+ fb = 2 # Index 2 框框
423
+ REMOVE_FOOT_NOTE = True # 是否丢弃掉 不是正文的内容 (比正文字体小,如参考文献、脚注、图注等)
424
+ REMOVE_FOOT_FFSIZE_PERCENT = 0.95 # 小于正文的?时,判定为不是正文(有些文章的正文部分字体大小不是100%统一的,有肉眼不可见的小变化)
425
+ def primary_ffsize(l):
426
+ """
427
+ 提取文本块主字体
428
+ """
429
+ fsize_statiscs = {}
430
+ for wtf in l['spans']:
431
+ if wtf['size'] not in fsize_statiscs: fsize_statiscs[wtf['size']] = 0
432
+ fsize_statiscs[wtf['size']] += len(wtf['text'])
433
+ return max(fsize_statiscs, key=fsize_statiscs.get)
434
+
435
+ def ffsize_same(a,b):
436
+ """
437
+ 提取字体大小是否近似相等
438
+ """
439
+ return abs((a-b)/max(a,b)) < 0.02
440
+
441
+ with fitz.open(fp) as doc:
442
+ meta_txt = []
443
+ meta_font = []
444
+
445
+ meta_line = []
446
+ meta_span = []
447
+ ############################## <第 1 步,搜集初始信息> ##################################
448
+ for index, page in enumerate(doc):
449
+ # file_content += page.get_text()
450
+ text_areas = page.get_text("dict") # 获取页面上的文本信息
451
+ for t in text_areas['blocks']:
452
+ if 'lines' in t:
453
+ pf = 998
454
+ for l in t['lines']:
455
+ txt_line = "".join([wtf['text'] for wtf in l['spans']])
456
+ if len(txt_line) == 0: continue
457
+ pf = primary_ffsize(l)
458
+ meta_line.append([txt_line, pf, l['bbox'], l])
459
+ for wtf in l['spans']: # for l in t['lines']:
460
+ meta_span.append([wtf['text'], wtf['size'], len(wtf['text'])])
461
+ # meta_line.append(["NEW_BLOCK", pf])
462
+ # 块元提取 for each word segment with in line for each line cross-line words for each block
463
+ meta_txt.extend([" ".join(["".join([wtf['text'] for wtf in l['spans']]) for l in t['lines']]).replace(
464
+ '- ', '') for t in text_areas['blocks'] if 'lines' in t])
465
+ meta_font.extend([np.mean([np.mean([wtf['size'] for wtf in l['spans']])
466
+ for l in t['lines']]) for t in text_areas['blocks'] if 'lines' in t])
467
+ if index == 0:
468
+ page_one_meta = [" ".join(["".join([wtf['text'] for wtf in l['spans']]) for l in t['lines']]).replace(
469
+ '- ', '') for t in text_areas['blocks'] if 'lines' in t]
470
+
471
+ ############################## <第 2 步,获取正文主字体> ##################################
472
+ fsize_statiscs = {}
473
+ for span in meta_span:
474
+ if span[1] not in fsize_statiscs: fsize_statiscs[span[1]] = 0
475
+ fsize_statiscs[span[1]] += span[2]
476
+ main_fsize = max(fsize_statiscs, key=fsize_statiscs.get)
477
+ if REMOVE_FOOT_NOTE:
478
+ give_up_fize_threshold = main_fsize * REMOVE_FOOT_FFSIZE_PERCENT
479
+
480
+ ############################## <第 3 步,切分和重新整合> ##################################
481
+ mega_sec = []
482
+ sec = []
483
+ for index, line in enumerate(meta_line):
484
+ if index == 0:
485
+ sec.append(line[fc])
486
+ continue
487
+ if REMOVE_FOOT_NOTE:
488
+ if meta_line[index][fs] <= give_up_fize_threshold:
489
+ continue
490
+ if ffsize_same(meta_line[index][fs], meta_line[index-1][fs]):
491
+ # 尝试识别段落
492
+ if meta_line[index][fc].endswith('.') and\
493
+ (meta_line[index-1][fc] != 'NEW_BLOCK') and \
494
+ (meta_line[index][fb][2] - meta_line[index][fb][0]) < (meta_line[index-1][fb][2] - meta_line[index-1][fb][0]) * 0.7:
495
+ sec[-1] += line[fc]
496
+ sec[-1] += "\n\n"
497
+ else:
498
+ sec[-1] += " "
499
+ sec[-1] += line[fc]
500
+ else:
501
+ if (index+1 < len(meta_line)) and \
502
+ meta_line[index][fs] > main_fsize:
503
+ # 单行 + 字体大
504
+ mega_sec.append(copy.deepcopy(sec))
505
+ sec = []
506
+ sec.append("# " + line[fc])
507
+ else:
508
+ # 尝试识别section
509
+ if meta_line[index-1][fs] > meta_line[index][fs]:
510
+ sec.append("\n" + line[fc])
511
+ else:
512
+ sec.append(line[fc])
513
+ mega_sec.append(copy.deepcopy(sec))
514
+
515
+ finals = []
516
+ for ms in mega_sec:
517
+ final = " ".join(ms)
518
+ final = final.replace('- ', ' ')
519
+ finals.append(final)
520
+ meta_txt = finals
521
+
522
+ ############################## <第 4 步,乱七八糟的后处理> ##################################
523
+ def 把字符太少的块清除为回车(meta_txt):
524
+ for index, block_txt in enumerate(meta_txt):
525
+ if len(block_txt) < 100:
526
+ meta_txt[index] = '\n'
527
+ return meta_txt
528
+ meta_txt = 把字符太少的块清除为回车(meta_txt)
529
+
530
+ def 清理多余的空行(meta_txt):
531
+ for index in reversed(range(1, len(meta_txt))):
532
+ if meta_txt[index] == '\n' and meta_txt[index-1] == '\n':
533
+ meta_txt.pop(index)
534
+ return meta_txt
535
+ meta_txt = 清理多余的空行(meta_txt)
536
+
537
+ def 合并小写开头的段落块(meta_txt):
538
+ def starts_with_lowercase_word(s):
539
+ pattern = r"^[a-z]+"
540
+ match = re.match(pattern, s)
541
+ if match:
542
+ return True
543
+ else:
544
+ return False
545
+ for _ in range(100):
546
+ for index, block_txt in enumerate(meta_txt):
547
+ if starts_with_lowercase_word(block_txt):
548
+ if meta_txt[index-1] != '\n':
549
+ meta_txt[index-1] += ' '
550
+ else:
551
+ meta_txt[index-1] = ''
552
+ meta_txt[index-1] += meta_txt[index]
553
+ meta_txt[index] = '\n'
554
+ return meta_txt
555
+ meta_txt = 合并小写开头的段落块(meta_txt)
556
+ meta_txt = 清理多余的空行(meta_txt)
557
+
558
+ meta_txt = '\n'.join(meta_txt)
559
+ # 清除重复的换行
560
+ for _ in range(5):
561
+ meta_txt = meta_txt.replace('\n\n', '\n')
562
+
563
+ # 换行 -> 双换行
564
+ meta_txt = meta_txt.replace('\n', '\n\n')
565
+
566
+ ############################## <第 5 步,展示分割效果> ##################################
567
+ # for f in finals:
568
+ # print亮黄(f)
569
+ # print亮绿('***************************')
570
+
571
+ return meta_txt, page_one_meta
572
+
573
+
574
+ def get_files_from_everything(txt, type): # type='.md'
575
+ """
576
+ 这个函数是用来获取指定目录下所有指定类型(如.md)的文件,并且对于网络上的文件,也可以获取它。
577
+ 下面是对每个参数和返回值的说明:
578
+ 参数
579
+ - txt: 路径或网址,表示要搜索的文件或者文件夹路径或网络上的文件。
580
+ - type: 字符串,表示要搜索的文件类型。默认是.md。
581
+ 返回值
582
+ - success: 布尔值,表示函数是否成功执行。
583
+ - file_manifest: 文件路径列表,里面包含以指定类型为后缀名的所有文件的绝对路径。
584
+ - project_folder: 字符串,表示文件所在的文件夹路径。如果是网络上的文件,就是临时文件夹的路径。
585
+ 该函数详细注释已添加,请确认是否满足您的需要。
586
+ """
587
+ import glob, os
588
+
589
+ success = True
590
+ if txt.startswith('http'):
591
+ # 网络的远程文件
592
+ import requests
593
+ from toolbox import get_conf
594
+ from toolbox import get_log_folder, gen_time_str
595
+ proxies, = get_conf('proxies')
596
+ try:
597
+ r = requests.get(txt, proxies=proxies)
598
+ except:
599
+ raise ConnectionRefusedError(f"无法下载资源{txt},请检查。")
600
+ path = os.path.join(get_log_folder(plugin_name='web_download'), gen_time_str()+type)
601
+ with open(path, 'wb+') as f: f.write(r.content)
602
+ project_folder = get_log_folder(plugin_name='web_download')
603
+ file_manifest = [path]
604
+ elif txt.endswith(type):
605
+ # 直接给定文件
606
+ file_manifest = [txt]
607
+ project_folder = os.path.dirname(txt)
608
+ elif os.path.exists(txt):
609
+ # 本地路径,递归搜索
610
+ project_folder = txt
611
+ file_manifest = [f for f in glob.glob(f'{project_folder}/**/*'+type, recursive=True)]
612
+ if len(file_manifest) == 0:
613
+ success = False
614
+ else:
615
+ project_folder = None
616
+ file_manifest = []
617
+ success = False
618
+
619
+ return success, file_manifest, project_folder
620
+
621
+
622
+
623
+
624
+ def Singleton(cls):
625
+ _instance = {}
626
+
627
+ def _singleton(*args, **kargs):
628
+ if cls not in _instance:
629
+ _instance[cls] = cls(*args, **kargs)
630
+ return _instance[cls]
631
+
632
+ return _singleton
633
+
634
+
635
+ @Singleton
636
+ class knowledge_archive_interface():
637
+ def __init__(self) -> None:
638
+ self.threadLock = threading.Lock()
639
+ self.current_id = ""
640
+ self.kai_path = None
641
+ self.qa_handle = None
642
+ self.text2vec_large_chinese = None
643
+
644
+ def get_chinese_text2vec(self):
645
+ if self.text2vec_large_chinese is None:
646
+ # < -------------------预热文本向量化模组--------------- >
647
+ from toolbox import ProxyNetworkActivate
648
+ print('Checking Text2vec ...')
649
+ from langchain.embeddings.huggingface import HuggingFaceEmbeddings
650
+ with ProxyNetworkActivate(): # 临时地激活代理网络
651
+ self.text2vec_large_chinese = HuggingFaceEmbeddings(model_name="GanymedeNil/text2vec-large-chinese")
652
+
653
+ return self.text2vec_large_chinese
654
+
655
+
656
+ def feed_archive(self, file_manifest, id="default"):
657
+ self.threadLock.acquire()
658
+ # import uuid
659
+ self.current_id = id
660
+ from zh_langchain import construct_vector_store
661
+ self.qa_handle, self.kai_path = construct_vector_store(
662
+ vs_id=self.current_id,
663
+ files=file_manifest,
664
+ sentence_size=100,
665
+ history=[],
666
+ one_conent="",
667
+ one_content_segmentation="",
668
+ text2vec = self.get_chinese_text2vec(),
669
+ )
670
+ self.threadLock.release()
671
+
672
+ def get_current_archive_id(self):
673
+ return self.current_id
674
+
675
+ def get_loaded_file(self):
676
+ return self.qa_handle.get_loaded_file()
677
+
678
+ def answer_with_archive_by_id(self, txt, id):
679
+ self.threadLock.acquire()
680
+ if not self.current_id == id:
681
+ self.current_id = id
682
+ from zh_langchain import construct_vector_store
683
+ self.qa_handle, self.kai_path = construct_vector_store(
684
+ vs_id=self.current_id,
685
+ files=[],
686
+ sentence_size=100,
687
+ history=[],
688
+ one_conent="",
689
+ one_content_segmentation="",
690
+ text2vec = self.get_chinese_text2vec(),
691
+ )
692
+ VECTOR_SEARCH_SCORE_THRESHOLD = 0
693
+ VECTOR_SEARCH_TOP_K = 4
694
+ CHUNK_SIZE = 512
695
+ resp, prompt = self.qa_handle.get_knowledge_based_conent_test(
696
+ query = txt,
697
+ vs_path = self.kai_path,
698
+ score_threshold=VECTOR_SEARCH_SCORE_THRESHOLD,
699
+ vector_search_top_k=VECTOR_SEARCH_TOP_K,
700
+ chunk_conent=True,
701
+ chunk_size=CHUNK_SIZE,
702
+ text2vec = self.get_chinese_text2vec(),
703
+ )
704
+ self.threadLock.release()
705
+ return resp, prompt
706
+
707
+ def try_install_deps(deps):
708
+ for dep in deps:
709
+ import subprocess, sys
710
+ subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--user', dep])
711
+
712
+
713
+ class construct_html():
714
+ def __init__(self) -> None:
715
+ self.css = """
716
+ .row {
717
+ display: flex;
718
+ flex-wrap: wrap;
719
+ }
720
+
721
+ .column {
722
+ flex: 1;
723
+ padding: 10px;
724
+ }
725
+
726
+ .table-header {
727
+ font-weight: bold;
728
+ border-bottom: 1px solid black;
729
+ }
730
+
731
+ .table-row {
732
+ border-bottom: 1px solid lightgray;
733
+ }
734
+
735
+ .table-cell {
736
+ padding: 5px;
737
+ }
738
+ """
739
+ self.html_string = f'<!DOCTYPE html><head><meta charset="utf-8"><title>翻译结果</title><style>{self.css}</style></head>'
740
+
741
+
742
+ def add_row(self, a, b):
743
+ tmp = """
744
+ <div class="row table-row">
745
+ <div class="column table-cell">REPLACE_A</div>
746
+ <div class="column table-cell">REPLACE_B</div>
747
+ </div>
748
+ """
749
+ from toolbox import markdown_convertion
750
+ tmp = tmp.replace('REPLACE_A', markdown_convertion(a))
751
+ tmp = tmp.replace('REPLACE_B', markdown_convertion(b))
752
+ self.html_string += tmp
753
+
754
+
755
+ def save_file(self, file_name):
756
+ with open(f'./gpt_log/{file_name}', 'w', encoding='utf8') as f:
757
+ f.write(self.html_string.encode('utf-8', 'ignore').decode())
758
+
crazy_functions/json_fns/pydantic_io.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ https://github.com/langchain-ai/langchain/blob/master/docs/extras/modules/model_io/output_parsers/pydantic.ipynb
3
+
4
+ Example 1.
5
+
6
+ # Define your desired data structure.
7
+ class Joke(BaseModel):
8
+ setup: str = Field(description="question to set up a joke")
9
+ punchline: str = Field(description="answer to resolve the joke")
10
+
11
+ # You can add custom validation logic easily with Pydantic.
12
+ @validator("setup")
13
+ def question_ends_with_question_mark(cls, field):
14
+ if field[-1] != "?":
15
+ raise ValueError("Badly formed question!")
16
+ return field
17
+
18
+
19
+ Example 2.
20
+
21
+ # Here's another example, but with a compound typed field.
22
+ class Actor(BaseModel):
23
+ name: str = Field(description="name of an actor")
24
+ film_names: List[str] = Field(description="list of names of films they starred in")
25
+ """
26
+
27
+ import json, re, logging
28
+
29
+
30
+ PYDANTIC_FORMAT_INSTRUCTIONS = """The output should be formatted as a JSON instance that conforms to the JSON schema below.
31
+
32
+ As an example, for the schema {{"properties": {{"foo": {{"title": "Foo", "description": "a list of strings", "type": "array", "items": {{"type": "string"}}}}}}, "required": ["foo"]}}
33
+ the object {{"foo": ["bar", "baz"]}} is a well-formatted instance of the schema. The object {{"properties": {{"foo": ["bar", "baz"]}}}} is not well-formatted.
34
+
35
+ Here is the output schema:
36
+ ```
37
+ {schema}
38
+ ```"""
39
+
40
+
41
+ PYDANTIC_FORMAT_INSTRUCTIONS_SIMPLE = """The output should be formatted as a JSON instance that conforms to the JSON schema below.
42
+ ```
43
+ {schema}
44
+ ```"""
45
+
46
+ class JsonStringError(Exception): ...
47
+
48
+ class GptJsonIO():
49
+
50
+ def __init__(self, schema, example_instruction=True):
51
+ self.pydantic_object = schema
52
+ self.example_instruction = example_instruction
53
+ self.format_instructions = self.generate_format_instructions()
54
+
55
+ def generate_format_instructions(self):
56
+ schema = self.pydantic_object.schema()
57
+
58
+ # Remove extraneous fields.
59
+ reduced_schema = schema
60
+ if "title" in reduced_schema:
61
+ del reduced_schema["title"]
62
+ if "type" in reduced_schema:
63
+ del reduced_schema["type"]
64
+ # Ensure json in context is well-formed with double quotes.
65
+ if self.example_instruction:
66
+ schema_str = json.dumps(reduced_schema)
67
+ return PYDANTIC_FORMAT_INSTRUCTIONS.format(schema=schema_str)
68
+ else:
69
+ return PYDANTIC_FORMAT_INSTRUCTIONS_SIMPLE.format(schema=schema_str)
70
+
71
+ def generate_output(self, text):
72
+ # Greedy search for 1st json candidate.
73
+ match = re.search(
74
+ r"\{.*\}", text.strip(), re.MULTILINE | re.IGNORECASE | re.DOTALL
75
+ )
76
+ json_str = ""
77
+ if match: json_str = match.group()
78
+ json_object = json.loads(json_str, strict=False)
79
+ final_object = self.pydantic_object.parse_obj(json_object)
80
+ return final_object
81
+
82
+ def generate_repair_prompt(self, broken_json, error):
83
+ prompt = "Fix a broken json string.\n\n" + \
84
+ "(1) The broken json string need to fix is: \n\n" + \
85
+ "```" + "\n" + \
86
+ broken_json + "\n" + \
87
+ "```" + "\n\n" + \
88
+ "(2) The error message is: \n\n" + \
89
+ error + "\n\n" + \
90
+ "Now, fix this json string. \n\n"
91
+ return prompt
92
+
93
+ def generate_output_auto_repair(self, response, gpt_gen_fn):
94
+ """
95
+ response: string containing canidate json
96
+ gpt_gen_fn: gpt_gen_fn(inputs, sys_prompt)
97
+ """
98
+ try:
99
+ result = self.generate_output(response)
100
+ except Exception as e:
101
+ try:
102
+ logging.info(f'Repairing json:{response}')
103
+ repair_prompt = self.generate_repair_prompt(broken_json = response, error=repr(e))
104
+ result = self.generate_output(gpt_gen_fn(repair_prompt, self.format_instructions))
105
+ logging.info('Repaire json success.')
106
+ except Exception as e:
107
+ # 没辙了,放弃治疗
108
+ logging.info('Repaire json fail.')
109
+ raise JsonStringError('Cannot repair json.', str(e))
110
+ return result
111
+
crazy_functions/latex_fns/latex_actions.py ADDED
@@ -0,0 +1,447 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import update_ui, update_ui_lastest_msg # 刷新Gradio前端界面
2
+ from toolbox import zip_folder, objdump, objload, promote_file_to_downloadzone
3
+ from .latex_toolbox import PRESERVE, TRANSFORM
4
+ from .latex_toolbox import set_forbidden_text, set_forbidden_text_begin_end, set_forbidden_text_careful_brace
5
+ from .latex_toolbox import reverse_forbidden_text_careful_brace, reverse_forbidden_text, convert_to_linklist, post_process
6
+ from .latex_toolbox import fix_content, find_main_tex_file, merge_tex_files, compile_latex_with_timeout
7
+
8
+ import os, shutil
9
+ import re
10
+ import numpy as np
11
+
12
+ pj = os.path.join
13
+
14
+
15
+ def split_subprocess(txt, project_folder, return_dict, opts):
16
+ """
17
+ break down latex file to a linked list,
18
+ each node use a preserve flag to indicate whether it should
19
+ be proccessed by GPT.
20
+ """
21
+ text = txt
22
+ mask = np.zeros(len(txt), dtype=np.uint8) + TRANSFORM
23
+
24
+ # 吸收title与作者以上的部分
25
+ text, mask = set_forbidden_text(text, mask, r"^(.*?)\\maketitle", re.DOTALL)
26
+ text, mask = set_forbidden_text(text, mask, r"^(.*?)\\begin{document}", re.DOTALL)
27
+ # 吸收iffalse注释
28
+ text, mask = set_forbidden_text(text, mask, r"\\iffalse(.*?)\\fi", re.DOTALL)
29
+ # 吸收在42行以内的begin-end组合
30
+ text, mask = set_forbidden_text_begin_end(text, mask, r"\\begin\{([a-z\*]*)\}(.*?)\\end\{\1\}", re.DOTALL, limit_n_lines=42)
31
+ # 吸收匿名公式
32
+ text, mask = set_forbidden_text(text, mask, [ r"\$\$([^$]+)\$\$", r"\\\[.*?\\\]" ], re.DOTALL)
33
+ # 吸收其他杂项
34
+ text, mask = set_forbidden_text(text, mask, [ r"\\section\{(.*?)\}", r"\\section\*\{(.*?)\}", r"\\subsection\{(.*?)\}", r"\\subsubsection\{(.*?)\}" ])
35
+ text, mask = set_forbidden_text(text, mask, [ r"\\bibliography\{(.*?)\}", r"\\bibliographystyle\{(.*?)\}" ])
36
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{thebibliography\}.*?\\end\{thebibliography\}", re.DOTALL)
37
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{lstlisting\}(.*?)\\end\{lstlisting\}", re.DOTALL)
38
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{wraptable\}(.*?)\\end\{wraptable\}", re.DOTALL)
39
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{algorithm\}(.*?)\\end\{algorithm\}", re.DOTALL)
40
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{wrapfigure\}(.*?)\\end\{wrapfigure\}", r"\\begin\{wrapfigure\*\}(.*?)\\end\{wrapfigure\*\}"], re.DOTALL)
41
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{figure\}(.*?)\\end\{figure\}", r"\\begin\{figure\*\}(.*?)\\end\{figure\*\}"], re.DOTALL)
42
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{multline\}(.*?)\\end\{multline\}", r"\\begin\{multline\*\}(.*?)\\end\{multline\*\}"], re.DOTALL)
43
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{table\}(.*?)\\end\{table\}", r"\\begin\{table\*\}(.*?)\\end\{table\*\}"], re.DOTALL)
44
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{minipage\}(.*?)\\end\{minipage\}", r"\\begin\{minipage\*\}(.*?)\\end\{minipage\*\}"], re.DOTALL)
45
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{align\*\}(.*?)\\end\{align\*\}", r"\\begin\{align\}(.*?)\\end\{align\}"], re.DOTALL)
46
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{equation\}(.*?)\\end\{equation\}", r"\\begin\{equation\*\}(.*?)\\end\{equation\*\}"], re.DOTALL)
47
+ text, mask = set_forbidden_text(text, mask, [r"\\includepdf\[(.*?)\]\{(.*?)\}", r"\\clearpage", r"\\newpage", r"\\appendix", r"\\tableofcontents", r"\\include\{(.*?)\}"])
48
+ text, mask = set_forbidden_text(text, mask, [r"\\vspace\{(.*?)\}", r"\\hspace\{(.*?)\}", r"\\label\{(.*?)\}", r"\\begin\{(.*?)\}", r"\\end\{(.*?)\}", r"\\item "])
49
+ text, mask = set_forbidden_text_careful_brace(text, mask, r"\\hl\{(.*?)\}", re.DOTALL)
50
+ # reverse 操作必须放在最后
51
+ text, mask = reverse_forbidden_text_careful_brace(text, mask, r"\\caption\{(.*?)\}", re.DOTALL, forbid_wrapper=True)
52
+ text, mask = reverse_forbidden_text_careful_brace(text, mask, r"\\abstract\{(.*?)\}", re.DOTALL, forbid_wrapper=True)
53
+ text, mask = reverse_forbidden_text(text, mask, r"\\begin\{abstract\}(.*?)\\end\{abstract\}", re.DOTALL, forbid_wrapper=True)
54
+ root = convert_to_linklist(text, mask)
55
+
56
+ # 最后一步处理,增强稳健性
57
+ root = post_process(root)
58
+
59
+ # 输出html调试文件,用红色标注处保留区(PRESERVE),用黑色标注转换区(TRANSFORM)
60
+ with open(pj(project_folder, 'debug_log.html'), 'w', encoding='utf8') as f:
61
+ segment_parts_for_gpt = []
62
+ nodes = []
63
+ node = root
64
+ while True:
65
+ nodes.append(node)
66
+ show_html = node.string.replace('\n','<br/>')
67
+ if not node.preserve:
68
+ segment_parts_for_gpt.append(node.string)
69
+ f.write(f'<p style="color:black;">#{node.range}{show_html}#</p>')
70
+ else:
71
+ f.write(f'<p style="color:red;">{show_html}</p>')
72
+ node = node.next
73
+ if node is None: break
74
+
75
+ for n in nodes: n.next = None # break
76
+ return_dict['nodes'] = nodes
77
+ return_dict['segment_parts_for_gpt'] = segment_parts_for_gpt
78
+ return return_dict
79
+
80
+ class LatexPaperSplit():
81
+ """
82
+ break down latex file to a linked list,
83
+ each node use a preserve flag to indicate whether it should
84
+ be proccessed by GPT.
85
+ """
86
+ def __init__(self) -> None:
87
+ self.nodes = None
88
+ self.msg = "*{\\scriptsize\\textbf{警告:该PDF由GPT-Academic开源项目调用大语言模型+Latex翻译插件一键生成," + \
89
+ "版权归原文作者所有。翻译内容可靠性无保障,请仔细鉴别并以原文为准。" + \
90
+ "项目Github地址 \\url{https://github.com/binary-husky/gpt_academic/}。"
91
+ # 请您不要删除或修改这行警告,除非您是论文的原作者(如果您是论文原作者,欢迎加REAME中的QQ联系开发者)
92
+ self.msg_declare = "为了防止大语言模型的意外谬误产生扩散影响,禁止移除或修改此警告。}}\\\\"
93
+
94
+
95
+ def merge_result(self, arr, mode, msg, buggy_lines=[], buggy_line_surgery_n_lines=10):
96
+ """
97
+ Merge the result after the GPT process completed
98
+ """
99
+ result_string = ""
100
+ node_cnt = 0
101
+ line_cnt = 0
102
+
103
+ for node in self.nodes:
104
+ if node.preserve:
105
+ line_cnt += node.string.count('\n')
106
+ result_string += node.string
107
+ else:
108
+ translated_txt = fix_content(arr[node_cnt], node.string)
109
+ begin_line = line_cnt
110
+ end_line = line_cnt + translated_txt.count('\n')
111
+
112
+ # reverse translation if any error
113
+ if any([begin_line-buggy_line_surgery_n_lines <= b_line <= end_line+buggy_line_surgery_n_lines for b_line in buggy_lines]):
114
+ translated_txt = node.string
115
+
116
+ result_string += translated_txt
117
+ node_cnt += 1
118
+ line_cnt += translated_txt.count('\n')
119
+
120
+ if mode == 'translate_zh':
121
+ pattern = re.compile(r'\\begin\{abstract\}.*\n')
122
+ match = pattern.search(result_string)
123
+ if not match:
124
+ # match \abstract{xxxx}
125
+ pattern_compile = re.compile(r"\\abstract\{(.*?)\}", flags=re.DOTALL)
126
+ match = pattern_compile.search(result_string)
127
+ position = match.regs[1][0]
128
+ else:
129
+ # match \begin{abstract}xxxx\end{abstract}
130
+ position = match.end()
131
+ result_string = result_string[:position] + self.msg + msg + self.msg_declare + result_string[position:]
132
+ return result_string
133
+
134
+
135
+ def split(self, txt, project_folder, opts):
136
+ """
137
+ break down latex file to a linked list,
138
+ each node use a preserve flag to indicate whether it should
139
+ be proccessed by GPT.
140
+ P.S. use multiprocessing to avoid timeout error
141
+ """
142
+ import multiprocessing
143
+ manager = multiprocessing.Manager()
144
+ return_dict = manager.dict()
145
+ p = multiprocessing.Process(
146
+ target=split_subprocess,
147
+ args=(txt, project_folder, return_dict, opts))
148
+ p.start()
149
+ p.join()
150
+ p.close()
151
+ self.nodes = return_dict['nodes']
152
+ self.sp = return_dict['segment_parts_for_gpt']
153
+ return self.sp
154
+
155
+
156
+ class LatexPaperFileGroup():
157
+ """
158
+ use tokenizer to break down text according to max_token_limit
159
+ """
160
+ def __init__(self):
161
+ self.file_paths = []
162
+ self.file_contents = []
163
+ self.sp_file_contents = []
164
+ self.sp_file_index = []
165
+ self.sp_file_tag = []
166
+
167
+ # count_token
168
+ from request_llm.bridge_all import model_info
169
+ enc = model_info["gpt-3.5-turbo"]['tokenizer']
170
+ def get_token_num(txt): return len(enc.encode(txt, disallowed_special=()))
171
+ self.get_token_num = get_token_num
172
+
173
+ def run_file_split(self, max_token_limit=1900):
174
+ """
175
+ use tokenizer to break down text according to max_token_limit
176
+ """
177
+ for index, file_content in enumerate(self.file_contents):
178
+ if self.get_token_num(file_content) < max_token_limit:
179
+ self.sp_file_contents.append(file_content)
180
+ self.sp_file_index.append(index)
181
+ self.sp_file_tag.append(self.file_paths[index])
182
+ else:
183
+ from ..crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf
184
+ segments = breakdown_txt_to_satisfy_token_limit_for_pdf(file_content, self.get_token_num, max_token_limit)
185
+ for j, segment in enumerate(segments):
186
+ self.sp_file_contents.append(segment)
187
+ self.sp_file_index.append(index)
188
+ self.sp_file_tag.append(self.file_paths[index] + f".part-{j}.tex")
189
+ print('Segmentation: done')
190
+
191
+ def merge_result(self):
192
+ self.file_result = ["" for _ in range(len(self.file_paths))]
193
+ for r, k in zip(self.sp_file_result, self.sp_file_index):
194
+ self.file_result[k] += r
195
+
196
+ def write_result(self):
197
+ manifest = []
198
+ for path, res in zip(self.file_paths, self.file_result):
199
+ with open(path + '.polish.tex', 'w', encoding='utf8') as f:
200
+ manifest.append(path + '.polish.tex')
201
+ f.write(res)
202
+ return manifest
203
+
204
+
205
+ def Latex精细分解与转化(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, mode='proofread', switch_prompt=None, opts=[]):
206
+ import time, os, re
207
+ from ..crazy_utils import request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency
208
+ from .latex_actions import LatexPaperFileGroup, LatexPaperSplit
209
+
210
+ # <-------- 寻找主tex文件 ---------->
211
+ maintex = find_main_tex_file(file_manifest, mode)
212
+ chatbot.append((f"定位主Latex文件", f'[Local Message] 分析结果:该项目的Latex主文件是{maintex}, 如果分析错误, 请立即终止程序, 删除或修改歧义文件, 然后重试。主程序即将开始, 请稍候。'))
213
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
214
+ time.sleep(3)
215
+
216
+ # <-------- 读取Latex文件, 将多文件tex工程融合为一个巨型tex ---------->
217
+ main_tex_basename = os.path.basename(maintex)
218
+ assert main_tex_basename.endswith('.tex')
219
+ main_tex_basename_bare = main_tex_basename[:-4]
220
+ may_exist_bbl = pj(project_folder, f'{main_tex_basename_bare}.bbl')
221
+ if os.path.exists(may_exist_bbl):
222
+ shutil.copyfile(may_exist_bbl, pj(project_folder, f'merge.bbl'))
223
+ shutil.copyfile(may_exist_bbl, pj(project_folder, f'merge_{mode}.bbl'))
224
+ shutil.copyfile(may_exist_bbl, pj(project_folder, f'merge_diff.bbl'))
225
+
226
+ with open(maintex, 'r', encoding='utf-8', errors='replace') as f:
227
+ content = f.read()
228
+ merged_content = merge_tex_files(project_folder, content, mode)
229
+
230
+ with open(project_folder + '/merge.tex', 'w', encoding='utf-8', errors='replace') as f:
231
+ f.write(merged_content)
232
+
233
+ # <-------- 精细切分latex文件 ---------->
234
+ chatbot.append((f"Latex文件融合完成", f'[Local Message] 正在精细切分latex文件,这需要一段时间计算,文档越长耗时越长,请耐心等待。'))
235
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
236
+ lps = LatexPaperSplit()
237
+ res = lps.split(merged_content, project_folder, opts) # 消耗时间的函数
238
+
239
+ # <-------- 拆分过长的latex片段 ---------->
240
+ pfg = LatexPaperFileGroup()
241
+ for index, r in enumerate(res):
242
+ pfg.file_paths.append('segment-' + str(index))
243
+ pfg.file_contents.append(r)
244
+
245
+ pfg.run_file_split(max_token_limit=1024)
246
+ n_split = len(pfg.sp_file_contents)
247
+
248
+ # <-------- 根据需要切换prompt ---------->
249
+ inputs_array, sys_prompt_array = switch_prompt(pfg, mode)
250
+ inputs_show_user_array = [f"{mode} {f}" for f in pfg.sp_file_tag]
251
+
252
+ if os.path.exists(pj(project_folder,'temp.pkl')):
253
+
254
+ # <-------- 【仅调试】如果存在调试缓存文件,则跳过GPT请求环节 ---------->
255
+ pfg = objload(file=pj(project_folder,'temp.pkl'))
256
+
257
+ else:
258
+ # <-------- gpt 多线程请求 ---------->
259
+ gpt_response_collection = yield from request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
260
+ inputs_array=inputs_array,
261
+ inputs_show_user_array=inputs_show_user_array,
262
+ llm_kwargs=llm_kwargs,
263
+ chatbot=chatbot,
264
+ history_array=[[""] for _ in range(n_split)],
265
+ sys_prompt_array=sys_prompt_array,
266
+ # max_workers=5, # 并行任务数量限制, 最多同时执行5个, 其他的排队等待
267
+ scroller_max_len = 40
268
+ )
269
+
270
+ # <-------- 文本碎片重组为完整的tex片段 ---------->
271
+ pfg.sp_file_result = []
272
+ for i_say, gpt_say, orig_content in zip(gpt_response_collection[0::2], gpt_response_collection[1::2], pfg.sp_file_contents):
273
+ pfg.sp_file_result.append(gpt_say)
274
+ pfg.merge_result()
275
+
276
+ # <-------- 临时存储用于调试 ---------->
277
+ pfg.get_token_num = None
278
+ objdump(pfg, file=pj(project_folder,'temp.pkl'))
279
+
280
+ write_html(pfg.sp_file_contents, pfg.sp_file_result, chatbot=chatbot, project_folder=project_folder)
281
+
282
+ # <-------- 写出文件 ---------->
283
+ msg = f"当前大语言模型: {llm_kwargs['llm_model']},当前语言模型温度设定: {llm_kwargs['temperature']}。"
284
+ final_tex = lps.merge_result(pfg.file_result, mode, msg)
285
+ objdump((lps, pfg.file_result, mode, msg), file=pj(project_folder,'merge_result.pkl'))
286
+
287
+ with open(project_folder + f'/merge_{mode}.tex', 'w', encoding='utf-8', errors='replace') as f:
288
+ if mode != 'translate_zh' or "binary" in final_tex: f.write(final_tex)
289
+
290
+
291
+ # <-------- 整理结果, 退出 ---------->
292
+ chatbot.append((f"完成了吗?", 'GPT结果已输出, 即将编译PDF'))
293
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
294
+
295
+ # <-------- 返回 ---------->
296
+ return project_folder + f'/merge_{mode}.tex'
297
+
298
+
299
+ def remove_buggy_lines(file_path, log_path, tex_name, tex_name_pure, n_fix, work_folder_modified, fixed_line=[]):
300
+ try:
301
+ with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
302
+ log = f.read()
303
+ import re
304
+ buggy_lines = re.findall(tex_name+':([0-9]{1,5}):', log)
305
+ buggy_lines = [int(l) for l in buggy_lines]
306
+ buggy_lines = sorted(buggy_lines)
307
+ buggy_line = buggy_lines[0]-1
308
+ print("reversing tex line that has errors", buggy_line)
309
+
310
+ # 重组,逆转出错的段落
311
+ if buggy_line not in fixed_line:
312
+ fixed_line.append(buggy_line)
313
+
314
+ lps, file_result, mode, msg = objload(file=pj(work_folder_modified,'merge_result.pkl'))
315
+ final_tex = lps.merge_result(file_result, mode, msg, buggy_lines=fixed_line, buggy_line_surgery_n_lines=5*n_fix)
316
+
317
+ with open(pj(work_folder_modified, f"{tex_name_pure}_fix_{n_fix}.tex"), 'w', encoding='utf-8', errors='replace') as f:
318
+ f.write(final_tex)
319
+
320
+ return True, f"{tex_name_pure}_fix_{n_fix}", buggy_lines
321
+ except:
322
+ print("Fatal error occurred, but we cannot identify error, please download zip, read latex log, and compile manually.")
323
+ return False, -1, [-1]
324
+
325
+
326
+ def 编译Latex(chatbot, history, main_file_original, main_file_modified, work_folder_original, work_folder_modified, work_folder, mode='default'):
327
+ import os, time
328
+ n_fix = 1
329
+ fixed_line = []
330
+ max_try = 32
331
+ chatbot.append([f"正在编译PDF文档", f'编译已经开始。当前工作路径为{work_folder},如果程序停顿5分钟以上,请直接去该路径下取回翻译结果,或者重启之后再度尝试 ...']); yield from update_ui(chatbot=chatbot, history=history)
332
+ chatbot.append([f"正在编译PDF文档", '...']); yield from update_ui(chatbot=chatbot, history=history); time.sleep(1); chatbot[-1] = list(chatbot[-1]) # 刷新界面
333
+ yield from update_ui_lastest_msg('编译已经开始...', chatbot, history) # 刷新Gradio前端界面
334
+
335
+ while True:
336
+ import os
337
+ may_exist_bbl = pj(work_folder_modified, f'merge.bbl')
338
+ target_bbl = pj(work_folder_modified, f'{main_file_modified}.bbl')
339
+ if os.path.exists(may_exist_bbl) and not os.path.exists(target_bbl):
340
+ shutil.copyfile(may_exist_bbl, target_bbl)
341
+
342
+ # https://stackoverflow.com/questions/738755/dont-make-me-manually-abort-a-latex-compile-when-theres-an-error
343
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译原始PDF ...', chatbot, history) # 刷新Gradio前端界面
344
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_original}.tex', work_folder_original)
345
+
346
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译转化后的PDF ...', chatbot, history) # 刷新Gradio前端界面
347
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_modified}.tex', work_folder_modified)
348
+
349
+ if ok and os.path.exists(pj(work_folder_modified, f'{main_file_modified}.pdf')):
350
+ # 只有第二步成功,才能继续下面的步骤
351
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译BibTex ...', chatbot, history) # 刷新Gradio前端界面
352
+ if not os.path.exists(pj(work_folder_original, f'{main_file_original}.bbl')):
353
+ ok = compile_latex_with_timeout(f'bibtex {main_file_original}.aux', work_folder_original)
354
+ if not os.path.exists(pj(work_folder_modified, f'{main_file_modified}.bbl')):
355
+ ok = compile_latex_with_timeout(f'bibtex {main_file_modified}.aux', work_folder_modified)
356
+
357
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译文献交叉引用 ...', chatbot, history) # 刷新Gradio前端界面
358
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_original}.tex', work_folder_original)
359
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_modified}.tex', work_folder_modified)
360
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_original}.tex', work_folder_original)
361
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_modified}.tex', work_folder_modified)
362
+
363
+ if mode!='translate_zh':
364
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 使用latexdiff生成论文转化前后对比 ...', chatbot, history) # 刷新Gradio前端界面
365
+ print( f'latexdiff --encoding=utf8 --append-safecmd=subfile {work_folder_original}/{main_file_original}.tex {work_folder_modified}/{main_file_modified}.tex --flatten > {work_folder}/merge_diff.tex')
366
+ ok = compile_latex_with_timeout(f'latexdiff --encoding=utf8 --append-safecmd=subfile {work_folder_original}/{main_file_original}.tex {work_folder_modified}/{main_file_modified}.tex --flatten > {work_folder}/merge_diff.tex')
367
+
368
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 正在编译对比PDF ...', chatbot, history) # 刷新Gradio前端界面
369
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
370
+ ok = compile_latex_with_timeout(f'bibtex merge_diff.aux', work_folder)
371
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
372
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
373
+
374
+ # <---------- 检查结果 ----------->
375
+ results_ = ""
376
+ original_pdf_success = os.path.exists(pj(work_folder_original, f'{main_file_original}.pdf'))
377
+ modified_pdf_success = os.path.exists(pj(work_folder_modified, f'{main_file_modified}.pdf'))
378
+ diff_pdf_success = os.path.exists(pj(work_folder, f'merge_diff.pdf'))
379
+ results_ += f"原始PDF编译是否成功: {original_pdf_success};"
380
+ results_ += f"转化PDF编译是否成功: {modified_pdf_success};"
381
+ results_ += f"对比PDF编译是否成功: {diff_pdf_success};"
382
+ yield from update_ui_lastest_msg(f'第{n_fix}编译结束:<br/>{results_}...', chatbot, history) # 刷新Gradio前端界面
383
+
384
+ if diff_pdf_success:
385
+ result_pdf = pj(work_folder_modified, f'merge_diff.pdf') # get pdf path
386
+ promote_file_to_downloadzone(result_pdf, rename_file=None, chatbot=chatbot) # promote file to web UI
387
+ if modified_pdf_success:
388
+ yield from update_ui_lastest_msg(f'转化PDF编译已经成功, 即将退出 ...', chatbot, history) # 刷新Gradio前端界面
389
+ result_pdf = pj(work_folder_modified, f'{main_file_modified}.pdf') # get pdf path
390
+ origin_pdf = pj(work_folder_original, f'{main_file_original}.pdf') # get pdf path
391
+ if os.path.exists(pj(work_folder, '..', 'translation')):
392
+ shutil.copyfile(result_pdf, pj(work_folder, '..', 'translation', 'translate_zh.pdf'))
393
+ promote_file_to_downloadzone(result_pdf, rename_file=None, chatbot=chatbot) # promote file to web UI
394
+ # 将两个PDF拼接
395
+ if original_pdf_success:
396
+ try:
397
+ from .latex_toolbox import merge_pdfs
398
+ concat_pdf = pj(work_folder_modified, f'comparison.pdf')
399
+ merge_pdfs(origin_pdf, result_pdf, concat_pdf)
400
+ promote_file_to_downloadzone(concat_pdf, rename_file=None, chatbot=chatbot) # promote file to web UI
401
+ except Exception as e:
402
+ pass
403
+ return True # 成功啦
404
+ else:
405
+ if n_fix>=max_try: break
406
+ n_fix += 1
407
+ can_retry, main_file_modified, buggy_lines = remove_buggy_lines(
408
+ file_path=pj(work_folder_modified, f'{main_file_modified}.tex'),
409
+ log_path=pj(work_folder_modified, f'{main_file_modified}.log'),
410
+ tex_name=f'{main_file_modified}.tex',
411
+ tex_name_pure=f'{main_file_modified}',
412
+ n_fix=n_fix,
413
+ work_folder_modified=work_folder_modified,
414
+ fixed_line=fixed_line
415
+ )
416
+ yield from update_ui_lastest_msg(f'由于最为关键的转化PDF编译失败, 将根据报错信息修正tex源文件并重试, 当前报错的latex代码处于第{buggy_lines}行 ...', chatbot, history) # 刷新Gradio前端界面
417
+ if not can_retry: break
418
+
419
+ return False # 失败啦
420
+
421
+
422
+ def write_html(sp_file_contents, sp_file_result, chatbot, project_folder):
423
+ # write html
424
+ try:
425
+ import shutil
426
+ from ..crazy_utils import construct_html
427
+ from toolbox import gen_time_str
428
+ ch = construct_html()
429
+ orig = ""
430
+ trans = ""
431
+ final = []
432
+ for c,r in zip(sp_file_contents, sp_file_result):
433
+ final.append(c)
434
+ final.append(r)
435
+ for i, k in enumerate(final):
436
+ if i%2==0:
437
+ orig = k
438
+ if i%2==1:
439
+ trans = k
440
+ ch.add_row(a=orig, b=trans)
441
+ create_report_file_name = f"{gen_time_str()}.trans.html"
442
+ ch.save_file(create_report_file_name)
443
+ shutil.copyfile(pj('./gpt_log/', create_report_file_name), pj(project_folder, create_report_file_name))
444
+ promote_file_to_downloadzone(file=f'./gpt_log/{create_report_file_name}', chatbot=chatbot)
445
+ except:
446
+ from toolbox import trimmed_format_exc
447
+ print('writing html result failed:', trimmed_format_exc())
crazy_functions/latex_fns/latex_toolbox.py ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, shutil
2
+ import re
3
+ import numpy as np
4
+ PRESERVE = 0
5
+ TRANSFORM = 1
6
+
7
+ pj = os.path.join
8
+
9
+ class LinkedListNode():
10
+ """
11
+ Linked List Node
12
+ """
13
+ def __init__(self, string, preserve=True) -> None:
14
+ self.string = string
15
+ self.preserve = preserve
16
+ self.next = None
17
+ self.range = None
18
+ # self.begin_line = 0
19
+ # self.begin_char = 0
20
+
21
+ def convert_to_linklist(text, mask):
22
+ root = LinkedListNode("", preserve=True)
23
+ current_node = root
24
+ for c, m, i in zip(text, mask, range(len(text))):
25
+ if (m==PRESERVE and current_node.preserve) \
26
+ or (m==TRANSFORM and not current_node.preserve):
27
+ # add
28
+ current_node.string += c
29
+ else:
30
+ current_node.next = LinkedListNode(c, preserve=(m==PRESERVE))
31
+ current_node = current_node.next
32
+ return root
33
+
34
+ def post_process(root):
35
+ # 修复括号
36
+ node = root
37
+ while True:
38
+ string = node.string
39
+ if node.preserve:
40
+ node = node.next
41
+ if node is None: break
42
+ continue
43
+ def break_check(string):
44
+ str_stack = [""] # (lv, index)
45
+ for i, c in enumerate(string):
46
+ if c == '{':
47
+ str_stack.append('{')
48
+ elif c == '}':
49
+ if len(str_stack) == 1:
50
+ print('stack fix')
51
+ return i
52
+ str_stack.pop(-1)
53
+ else:
54
+ str_stack[-1] += c
55
+ return -1
56
+ bp = break_check(string)
57
+
58
+ if bp == -1:
59
+ pass
60
+ elif bp == 0:
61
+ node.string = string[:1]
62
+ q = LinkedListNode(string[1:], False)
63
+ q.next = node.next
64
+ node.next = q
65
+ else:
66
+ node.string = string[:bp]
67
+ q = LinkedListNode(string[bp:], False)
68
+ q.next = node.next
69
+ node.next = q
70
+
71
+ node = node.next
72
+ if node is None: break
73
+
74
+ # 屏蔽空行和太短的句子
75
+ node = root
76
+ while True:
77
+ if len(node.string.strip('\n').strip(''))==0: node.preserve = True
78
+ if len(node.string.strip('\n').strip(''))<42: node.preserve = True
79
+ node = node.next
80
+ if node is None: break
81
+ node = root
82
+ while True:
83
+ if node.next and node.preserve and node.next.preserve:
84
+ node.string += node.next.string
85
+ node.next = node.next.next
86
+ node = node.next
87
+ if node is None: break
88
+
89
+ # 将前后断行符脱离
90
+ node = root
91
+ prev_node = None
92
+ while True:
93
+ if not node.preserve:
94
+ lstriped_ = node.string.lstrip().lstrip('\n')
95
+ if (prev_node is not None) and (prev_node.preserve) and (len(lstriped_)!=len(node.string)):
96
+ prev_node.string += node.string[:-len(lstriped_)]
97
+ node.string = lstriped_
98
+ rstriped_ = node.string.rstrip().rstrip('\n')
99
+ if (node.next is not None) and (node.next.preserve) and (len(rstriped_)!=len(node.string)):
100
+ node.next.string = node.string[len(rstriped_):] + node.next.string
101
+ node.string = rstriped_
102
+ # =====
103
+ prev_node = node
104
+ node = node.next
105
+ if node is None: break
106
+
107
+ # 标注节点的行数范围
108
+ node = root
109
+ n_line = 0
110
+ expansion = 2
111
+ while True:
112
+ n_l = node.string.count('\n')
113
+ node.range = [n_line-expansion, n_line+n_l+expansion] # 失败时,扭转的范围
114
+ n_line = n_line+n_l
115
+ node = node.next
116
+ if node is None: break
117
+ return root
118
+
119
+
120
+ """
121
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
122
+ Latex segmentation with a binary mask (PRESERVE=0, TRANSFORM=1)
123
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
124
+ """
125
+
126
+
127
+ def set_forbidden_text(text, mask, pattern, flags=0):
128
+ """
129
+ Add a preserve text area in this paper
130
+ e.g. with pattern = r"\\begin\{algorithm\}(.*?)\\end\{algorithm\}"
131
+ you can mask out (mask = PRESERVE so that text become untouchable for GPT)
132
+ everything between "\begin{equation}" and "\end{equation}"
133
+ """
134
+ if isinstance(pattern, list): pattern = '|'.join(pattern)
135
+ pattern_compile = re.compile(pattern, flags)
136
+ for res in pattern_compile.finditer(text):
137
+ mask[res.span()[0]:res.span()[1]] = PRESERVE
138
+ return text, mask
139
+
140
+ def reverse_forbidden_text(text, mask, pattern, flags=0, forbid_wrapper=True):
141
+ """
142
+ Move area out of preserve area (make text editable for GPT)
143
+ count the number of the braces so as to catch compelete text area.
144
+ e.g.
145
+ \begin{abstract} blablablablablabla. \end{abstract}
146
+ """
147
+ if isinstance(pattern, list): pattern = '|'.join(pattern)
148
+ pattern_compile = re.compile(pattern, flags)
149
+ for res in pattern_compile.finditer(text):
150
+ if not forbid_wrapper:
151
+ mask[res.span()[0]:res.span()[1]] = TRANSFORM
152
+ else:
153
+ mask[res.regs[0][0]: res.regs[1][0]] = PRESERVE # '\\begin{abstract}'
154
+ mask[res.regs[1][0]: res.regs[1][1]] = TRANSFORM # abstract
155
+ mask[res.regs[1][1]: res.regs[0][1]] = PRESERVE # abstract
156
+ return text, mask
157
+
158
+ def set_forbidden_text_careful_brace(text, mask, pattern, flags=0):
159
+ """
160
+ Add a preserve text area in this paper (text become untouchable for GPT).
161
+ count the number of the braces so as to catch compelete text area.
162
+ e.g.
163
+ \caption{blablablablabla\texbf{blablabla}blablabla.}
164
+ """
165
+ pattern_compile = re.compile(pattern, flags)
166
+ for res in pattern_compile.finditer(text):
167
+ brace_level = -1
168
+ p = begin = end = res.regs[0][0]
169
+ for _ in range(1024*16):
170
+ if text[p] == '}' and brace_level == 0: break
171
+ elif text[p] == '}': brace_level -= 1
172
+ elif text[p] == '{': brace_level += 1
173
+ p += 1
174
+ end = p+1
175
+ mask[begin:end] = PRESERVE
176
+ return text, mask
177
+
178
+ def reverse_forbidden_text_careful_brace(text, mask, pattern, flags=0, forbid_wrapper=True):
179
+ """
180
+ Move area out of preserve area (make text editable for GPT)
181
+ count the number of the braces so as to catch compelete text area.
182
+ e.g.
183
+ \caption{blablablablabla\texbf{blablabla}blablabla.}
184
+ """
185
+ pattern_compile = re.compile(pattern, flags)
186
+ for res in pattern_compile.finditer(text):
187
+ brace_level = 0
188
+ p = begin = end = res.regs[1][0]
189
+ for _ in range(1024*16):
190
+ if text[p] == '}' and brace_level == 0: break
191
+ elif text[p] == '}': brace_level -= 1
192
+ elif text[p] == '{': brace_level += 1
193
+ p += 1
194
+ end = p
195
+ mask[begin:end] = TRANSFORM
196
+ if forbid_wrapper:
197
+ mask[res.regs[0][0]:begin] = PRESERVE
198
+ mask[end:res.regs[0][1]] = PRESERVE
199
+ return text, mask
200
+
201
+ def set_forbidden_text_begin_end(text, mask, pattern, flags=0, limit_n_lines=42):
202
+ """
203
+ Find all \begin{} ... \end{} text block that with less than limit_n_lines lines.
204
+ Add it to preserve area
205
+ """
206
+ pattern_compile = re.compile(pattern, flags)
207
+ def search_with_line_limit(text, mask):
208
+ for res in pattern_compile.finditer(text):
209
+ cmd = res.group(1) # begin{what}
210
+ this = res.group(2) # content between begin and end
211
+ this_mask = mask[res.regs[2][0]:res.regs[2][1]]
212
+ white_list = ['document', 'abstract', 'lemma', 'definition', 'sproof',
213
+ 'em', 'emph', 'textit', 'textbf', 'itemize', 'enumerate']
214
+ if (cmd in white_list) or this.count('\n') >= limit_n_lines: # use a magical number 42
215
+ this, this_mask = search_with_line_limit(this, this_mask)
216
+ mask[res.regs[2][0]:res.regs[2][1]] = this_mask
217
+ else:
218
+ mask[res.regs[0][0]:res.regs[0][1]] = PRESERVE
219
+ return text, mask
220
+ return search_with_line_limit(text, mask)
221
+
222
+
223
+
224
+ """
225
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
226
+ Latex Merge File
227
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
228
+ """
229
+
230
+ def find_main_tex_file(file_manifest, mode):
231
+ """
232
+ 在多Tex文档中,寻找主文件,必须包含documentclass,返回找到的第一个。
233
+ P.S. 但愿没人把latex模板放在里面传进来 (6.25 加入判定latex模板的代码)
234
+ """
235
+ canidates = []
236
+ for texf in file_manifest:
237
+ if os.path.basename(texf).startswith('merge'):
238
+ continue
239
+ with open(texf, 'r', encoding='utf8', errors='ignore') as f:
240
+ file_content = f.read()
241
+ if r'\documentclass' in file_content:
242
+ canidates.append(texf)
243
+ else:
244
+ continue
245
+
246
+ if len(canidates) == 0:
247
+ raise RuntimeError('无法找到一个主Tex文件(包含documentclass关键字)')
248
+ elif len(canidates) == 1:
249
+ return canidates[0]
250
+ else: # if len(canidates) >= 2 通过一些Latex模板中常见(但通常不会出现在正文)的单词,对不同latex源文件扣分,取评分最高者返回
251
+ canidates_score = []
252
+ # 给出一些判定模板文档的词作为扣分项
253
+ unexpected_words = ['\LaTeX', 'manuscript', 'Guidelines', 'font', 'citations', 'rejected', 'blind review', 'reviewers']
254
+ expected_words = ['\input', '\ref', '\cite']
255
+ for texf in canidates:
256
+ canidates_score.append(0)
257
+ with open(texf, 'r', encoding='utf8', errors='ignore') as f:
258
+ file_content = f.read()
259
+ for uw in unexpected_words:
260
+ if uw in file_content:
261
+ canidates_score[-1] -= 1
262
+ for uw in expected_words:
263
+ if uw in file_content:
264
+ canidates_score[-1] += 1
265
+ select = np.argmax(canidates_score) # 取评分最高者返回
266
+ return canidates[select]
267
+
268
+ def rm_comments(main_file):
269
+ new_file_remove_comment_lines = []
270
+ for l in main_file.splitlines():
271
+ # 删除整行���空注释
272
+ if l.lstrip().startswith("%"):
273
+ pass
274
+ else:
275
+ new_file_remove_comment_lines.append(l)
276
+ main_file = '\n'.join(new_file_remove_comment_lines)
277
+ # main_file = re.sub(r"\\include{(.*?)}", r"\\input{\1}", main_file) # 将 \include 命令转换为 \input 命令
278
+ main_file = re.sub(r'(?<!\\)%.*', '', main_file) # 使用正则表达式查找半行注释, 并替换为空字符串
279
+ return main_file
280
+
281
+ def find_tex_file_ignore_case(fp):
282
+ dir_name = os.path.dirname(fp)
283
+ base_name = os.path.basename(fp)
284
+ # 如果输入的文件路径是正确的
285
+ if os.path.exists(pj(dir_name, base_name)): return pj(dir_name, base_name)
286
+ # 如果不正确,试着加上.tex后缀试试
287
+ if not base_name.endswith('.tex'): base_name+='.tex'
288
+ if os.path.exists(pj(dir_name, base_name)): return pj(dir_name, base_name)
289
+ # 如果还找不到,解除大小写限制,再试一次
290
+ import glob
291
+ for f in glob.glob(dir_name+'/*.tex'):
292
+ base_name_s = os.path.basename(fp)
293
+ if base_name_s.lower() == base_name.lower(): return f
294
+ return None
295
+
296
+ def merge_tex_files_(project_foler, main_file, mode):
297
+ """
298
+ Merge Tex project recrusively
299
+ """
300
+ main_file = rm_comments(main_file)
301
+ for s in reversed([q for q in re.finditer(r"\\input\{(.*?)\}", main_file, re.M)]):
302
+ f = s.group(1)
303
+ fp = os.path.join(project_foler, f)
304
+ fp = find_tex_file_ignore_case(fp)
305
+ if fp:
306
+ with open(fp, 'r', encoding='utf-8', errors='replace') as fx: c = fx.read()
307
+ else:
308
+ raise RuntimeError(f'找不到{fp},Tex源文件缺失!')
309
+ c = merge_tex_files_(project_foler, c, mode)
310
+ main_file = main_file[:s.span()[0]] + c + main_file[s.span()[1]:]
311
+ return main_file
312
+
313
+ def merge_tex_files(project_foler, main_file, mode):
314
+ """
315
+ Merge Tex project recrusively
316
+ P.S. 顺便把CTEX塞进去以支持中文
317
+ P.S. 顺便把Latex的注释去除
318
+ """
319
+ main_file = merge_tex_files_(project_foler, main_file, mode)
320
+ main_file = rm_comments(main_file)
321
+
322
+ if mode == 'translate_zh':
323
+ # find paper documentclass
324
+ pattern = re.compile(r'\\documentclass.*\n')
325
+ match = pattern.search(main_file)
326
+ assert match is not None, "Cannot find documentclass statement!"
327
+ position = match.end()
328
+ add_ctex = '\\usepackage{ctex}\n'
329
+ add_url = '\\usepackage{url}\n' if '{url}' not in main_file else ''
330
+ main_file = main_file[:position] + add_ctex + add_url + main_file[position:]
331
+ # fontset=windows
332
+ import platform
333
+ main_file = re.sub(r"\\documentclass\[(.*?)\]{(.*?)}", r"\\documentclass[\1,fontset=windows,UTF8]{\2}",main_file)
334
+ main_file = re.sub(r"\\documentclass{(.*?)}", r"\\documentclass[fontset=windows,UTF8]{\1}",main_file)
335
+ # find paper abstract
336
+ pattern_opt1 = re.compile(r'\\begin\{abstract\}.*\n')
337
+ pattern_opt2 = re.compile(r"\\abstract\{(.*?)\}", flags=re.DOTALL)
338
+ match_opt1 = pattern_opt1.search(main_file)
339
+ match_opt2 = pattern_opt2.search(main_file)
340
+ assert (match_opt1 is not None) or (match_opt2 is not None), "Cannot find paper abstract section!"
341
+ return main_file
342
+
343
+
344
+ """
345
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
346
+ Post process
347
+ =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
348
+ """
349
+ def mod_inbraket(match):
350
+ """
351
+ 为啥chatgpt会把cite里面的逗号换成中文逗号呀
352
+ """
353
+ # get the matched string
354
+ cmd = match.group(1)
355
+ str_to_modify = match.group(2)
356
+ # modify the matched string
357
+ str_to_modify = str_to_modify.replace(':', ':') # 前面是中文冒号,后面是英文冒号
358
+ str_to_modify = str_to_modify.replace(',', ',') # 前面是中文逗号,后面是英文逗号
359
+ # str_to_modify = 'BOOM'
360
+ return "\\" + cmd + "{" + str_to_modify + "}"
361
+
362
+ def fix_content(final_tex, node_string):
363
+ """
364
+ Fix common GPT errors to increase success rate
365
+ """
366
+ final_tex = re.sub(r"(?<!\\)%", "\\%", final_tex)
367
+ final_tex = re.sub(r"\\([a-z]{2,10})\ \{", r"\\\1{", string=final_tex)
368
+ final_tex = re.sub(r"\\\ ([a-z]{2,10})\{", r"\\\1{", string=final_tex)
369
+ final_tex = re.sub(r"\\([a-z]{2,10})\{([^\}]*?)\}", mod_inbraket, string=final_tex)
370
+
371
+ if "Traceback" in final_tex and "[Local Message]" in final_tex:
372
+ final_tex = node_string # 出问题了,还原原文
373
+ if node_string.count('\\begin') != final_tex.count('\\begin'):
374
+ final_tex = node_string # 出问题了,还原原文
375
+ if node_string.count('\_') > 0 and node_string.count('\_') > final_tex.count('\_'):
376
+ # walk and replace any _ without \
377
+ final_tex = re.sub(r"(?<!\\)_", "\\_", final_tex)
378
+
379
+ def compute_brace_level(string):
380
+ # this function count the number of { and }
381
+ brace_level = 0
382
+ for c in string:
383
+ if c == "{": brace_level += 1
384
+ elif c == "}": brace_level -= 1
385
+ return brace_level
386
+ def join_most(tex_t, tex_o):
387
+ # this function join translated string and original string when something goes wrong
388
+ p_t = 0
389
+ p_o = 0
390
+ def find_next(string, chars, begin):
391
+ p = begin
392
+ while p < len(string):
393
+ if string[p] in chars: return p, string[p]
394
+ p += 1
395
+ return None, None
396
+ while True:
397
+ res1, char = find_next(tex_o, ['{','}'], p_o)
398
+ if res1 is None: break
399
+ res2, char = find_next(tex_t, [char], p_t)
400
+ if res2 is None: break
401
+ p_o = res1 + 1
402
+ p_t = res2 + 1
403
+ return tex_t[:p_t] + tex_o[p_o:]
404
+
405
+ if compute_brace_level(final_tex) != compute_brace_level(node_string):
406
+ # 出问题了,还原部分原文,保证括号正确
407
+ final_tex = join_most(final_tex, node_string)
408
+ return final_tex
409
+
410
+ def compile_latex_with_timeout(command, cwd, timeout=60):
411
+ import subprocess
412
+ process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
413
+ try:
414
+ stdout, stderr = process.communicate(timeout=timeout)
415
+ except subprocess.TimeoutExpired:
416
+ process.kill()
417
+ stdout, stderr = process.communicate()
418
+ print("Process timed out!")
419
+ return False
420
+ return True
421
+
422
+
423
+
424
+ def merge_pdfs(pdf1_path, pdf2_path, output_path):
425
+ import PyPDF2
426
+ Percent = 0.8
427
+ # Open the first PDF file
428
+ with open(pdf1_path, 'rb') as pdf1_file:
429
+ pdf1_reader = PyPDF2.PdfFileReader(pdf1_file)
430
+ # Open the second PDF file
431
+ with open(pdf2_path, 'rb') as pdf2_file:
432
+ pdf2_reader = PyPDF2.PdfFileReader(pdf2_file)
433
+ # Create a new PDF file to store the merged pages
434
+ output_writer = PyPDF2.PdfFileWriter()
435
+ # Determine the number of pages in each PDF file
436
+ num_pages = max(pdf1_reader.numPages, pdf2_reader.numPages)
437
+ # Merge the pages from the two PDF files
438
+ for page_num in range(num_pages):
439
+ # Add the page from the first PDF file
440
+ if page_num < pdf1_reader.numPages:
441
+ page1 = pdf1_reader.getPage(page_num)
442
+ else:
443
+ page1 = PyPDF2.PageObject.createBlankPage(pdf1_reader)
444
+ # Add the page from the second PDF file
445
+ if page_num < pdf2_reader.numPages:
446
+ page2 = pdf2_reader.getPage(page_num)
447
+ else:
448
+ page2 = PyPDF2.PageObject.createBlankPage(pdf1_reader)
449
+ # Create a new empty page with double width
450
+ new_page = PyPDF2.PageObject.createBlankPage(
451
+ width = int(int(page1.mediaBox.getWidth()) + int(page2.mediaBox.getWidth()) * Percent),
452
+ height = max(page1.mediaBox.getHeight(), page2.mediaBox.getHeight())
453
+ )
454
+ new_page.mergeTranslatedPage(page1, 0, 0)
455
+ new_page.mergeTranslatedPage(page2, int(int(page1.mediaBox.getWidth())-int(page2.mediaBox.getWidth())* (1-Percent)), 0)
456
+ output_writer.addPage(new_page)
457
+ # Save the merged PDF file
458
+ with open(output_path, 'wb') as output_file:
459
+ output_writer.write(output_file)
crazy_functions/latex_utils.py ADDED
@@ -0,0 +1,788 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from toolbox import update_ui, update_ui_lastest_msg # 刷新Gradio前端界面
2
+ from toolbox import zip_folder, objdump, objload, promote_file_to_downloadzone
3
+ import os, shutil
4
+ import re
5
+ import numpy as np
6
+ pj = os.path.join
7
+
8
+ """
9
+ ========================================================================
10
+ Part One
11
+ Latex segmentation with a binary mask (PRESERVE=0, TRANSFORM=1)
12
+ ========================================================================
13
+ """
14
+ PRESERVE = 0
15
+ TRANSFORM = 1
16
+
17
+ def set_forbidden_text(text, mask, pattern, flags=0):
18
+ """
19
+ Add a preserve text area in this paper
20
+ e.g. with pattern = r"\\begin\{algorithm\}(.*?)\\end\{algorithm\}"
21
+ you can mask out (mask = PRESERVE so that text become untouchable for GPT)
22
+ everything between "\begin{equation}" and "\end{equation}"
23
+ """
24
+ if isinstance(pattern, list): pattern = '|'.join(pattern)
25
+ pattern_compile = re.compile(pattern, flags)
26
+ for res in pattern_compile.finditer(text):
27
+ mask[res.span()[0]:res.span()[1]] = PRESERVE
28
+ return text, mask
29
+
30
+ def reverse_forbidden_text(text, mask, pattern, flags=0, forbid_wrapper=True):
31
+ """
32
+ Move area out of preserve area (make text editable for GPT)
33
+ count the number of the braces so as to catch compelete text area.
34
+ e.g.
35
+ \begin{abstract} blablablablablabla. \end{abstract}
36
+ """
37
+ if isinstance(pattern, list): pattern = '|'.join(pattern)
38
+ pattern_compile = re.compile(pattern, flags)
39
+ for res in pattern_compile.finditer(text):
40
+ if not forbid_wrapper:
41
+ mask[res.span()[0]:res.span()[1]] = TRANSFORM
42
+ else:
43
+ mask[res.regs[0][0]: res.regs[1][0]] = PRESERVE # '\\begin{abstract}'
44
+ mask[res.regs[1][0]: res.regs[1][1]] = TRANSFORM # abstract
45
+ mask[res.regs[1][1]: res.regs[0][1]] = PRESERVE # abstract
46
+ return text, mask
47
+
48
+ def set_forbidden_text_careful_brace(text, mask, pattern, flags=0):
49
+ """
50
+ Add a preserve text area in this paper (text become untouchable for GPT).
51
+ count the number of the braces so as to catch compelete text area.
52
+ e.g.
53
+ \caption{blablablablabla\texbf{blablabla}blablabla.}
54
+ """
55
+ pattern_compile = re.compile(pattern, flags)
56
+ for res in pattern_compile.finditer(text):
57
+ brace_level = -1
58
+ p = begin = end = res.regs[0][0]
59
+ for _ in range(1024*16):
60
+ if text[p] == '}' and brace_level == 0: break
61
+ elif text[p] == '}': brace_level -= 1
62
+ elif text[p] == '{': brace_level += 1
63
+ p += 1
64
+ end = p+1
65
+ mask[begin:end] = PRESERVE
66
+ return text, mask
67
+
68
+ def reverse_forbidden_text_careful_brace(text, mask, pattern, flags=0, forbid_wrapper=True):
69
+ """
70
+ Move area out of preserve area (make text editable for GPT)
71
+ count the number of the braces so as to catch compelete text area.
72
+ e.g.
73
+ \caption{blablablablabla\texbf{blablabla}blablabla.}
74
+ """
75
+ pattern_compile = re.compile(pattern, flags)
76
+ for res in pattern_compile.finditer(text):
77
+ brace_level = 0
78
+ p = begin = end = res.regs[1][0]
79
+ for _ in range(1024*16):
80
+ if text[p] == '}' and brace_level == 0: break
81
+ elif text[p] == '}': brace_level -= 1
82
+ elif text[p] == '{': brace_level += 1
83
+ p += 1
84
+ end = p
85
+ mask[begin:end] = TRANSFORM
86
+ if forbid_wrapper:
87
+ mask[res.regs[0][0]:begin] = PRESERVE
88
+ mask[end:res.regs[0][1]] = PRESERVE
89
+ return text, mask
90
+
91
+ def set_forbidden_text_begin_end(text, mask, pattern, flags=0, limit_n_lines=42):
92
+ """
93
+ Find all \begin{} ... \end{} text block that with less than limit_n_lines lines.
94
+ Add it to preserve area
95
+ """
96
+ pattern_compile = re.compile(pattern, flags)
97
+ def search_with_line_limit(text, mask):
98
+ for res in pattern_compile.finditer(text):
99
+ cmd = res.group(1) # begin{what}
100
+ this = res.group(2) # content between begin and end
101
+ this_mask = mask[res.regs[2][0]:res.regs[2][1]]
102
+ white_list = ['document', 'abstract', 'lemma', 'definition', 'sproof',
103
+ 'em', 'emph', 'textit', 'textbf', 'itemize', 'enumerate']
104
+ if (cmd in white_list) or this.count('\n') >= limit_n_lines: # use a magical number 42
105
+ this, this_mask = search_with_line_limit(this, this_mask)
106
+ mask[res.regs[2][0]:res.regs[2][1]] = this_mask
107
+ else:
108
+ mask[res.regs[0][0]:res.regs[0][1]] = PRESERVE
109
+ return text, mask
110
+ return search_with_line_limit(text, mask)
111
+
112
+ class LinkedListNode():
113
+ """
114
+ Linked List Node
115
+ """
116
+ def __init__(self, string, preserve=True) -> None:
117
+ self.string = string
118
+ self.preserve = preserve
119
+ self.next = None
120
+ # self.begin_line = 0
121
+ # self.begin_char = 0
122
+
123
+ def convert_to_linklist(text, mask):
124
+ root = LinkedListNode("", preserve=True)
125
+ current_node = root
126
+ for c, m, i in zip(text, mask, range(len(text))):
127
+ if (m==PRESERVE and current_node.preserve) \
128
+ or (m==TRANSFORM and not current_node.preserve):
129
+ # add
130
+ current_node.string += c
131
+ else:
132
+ current_node.next = LinkedListNode(c, preserve=(m==PRESERVE))
133
+ current_node = current_node.next
134
+ return root
135
+ """
136
+ ========================================================================
137
+ Latex Merge File
138
+ ========================================================================
139
+ """
140
+
141
+ def 寻找Latex主文件(file_manifest, mode):
142
+ """
143
+ 在多Tex文档中,寻找主文件,必须包含documentclass,返回找到的第一个。
144
+ P.S. 但愿没人把latex模板放在里面传进来 (6.25 加入判定latex模板的代码)
145
+ """
146
+ canidates = []
147
+ for texf in file_manifest:
148
+ if os.path.basename(texf).startswith('merge'):
149
+ continue
150
+ with open(texf, 'r', encoding='utf8') as f:
151
+ file_content = f.read()
152
+ if r'\documentclass' in file_content:
153
+ canidates.append(texf)
154
+ else:
155
+ continue
156
+
157
+ if len(canidates) == 0:
158
+ raise RuntimeError('无法找到一个主Tex文件(包含documentclass关键字)')
159
+ elif len(canidates) == 1:
160
+ return canidates[0]
161
+ else: # if len(canidates) >= 2 通过一些Latex模板中常见(但通常不会出现在正文)的单词,对不同latex源文件扣分,取评分最高者返回
162
+ canidates_score = []
163
+ # 给出一些判定模板文档的词作为扣分项
164
+ unexpected_words = ['\LaTeX', 'manuscript', 'Guidelines', 'font', 'citations', 'rejected', 'blind review', 'reviewers']
165
+ expected_words = ['\input', '\ref', '\cite']
166
+ for texf in canidates:
167
+ canidates_score.append(0)
168
+ with open(texf, 'r', encoding='utf8') as f:
169
+ file_content = f.read()
170
+ for uw in unexpected_words:
171
+ if uw in file_content:
172
+ canidates_score[-1] -= 1
173
+ for uw in expected_words:
174
+ if uw in file_content:
175
+ canidates_score[-1] += 1
176
+ select = np.argmax(canidates_score) # 取评分最高者返回
177
+ return canidates[select]
178
+
179
+ def rm_comments(main_file):
180
+ new_file_remove_comment_lines = []
181
+ for l in main_file.splitlines():
182
+ # 删除整行的空注释
183
+ if l.lstrip().startswith("%"):
184
+ pass
185
+ else:
186
+ new_file_remove_comment_lines.append(l)
187
+ main_file = '\n'.join(new_file_remove_comment_lines)
188
+ # main_file = re.sub(r"\\include{(.*?)}", r"\\input{\1}", main_file) # 将 \include 命令转换为 \input 命令
189
+ main_file = re.sub(r'(?<!\\)%.*', '', main_file) # 使用正则表达式查找半行注释, 并替换为空字符串
190
+ return main_file
191
+
192
+ def merge_tex_files_(project_foler, main_file, mode):
193
+ """
194
+ Merge Tex project recrusively
195
+ """
196
+ main_file = rm_comments(main_file)
197
+ for s in reversed([q for q in re.finditer(r"\\input\{(.*?)\}", main_file, re.M)]):
198
+ f = s.group(1)
199
+ fp = os.path.join(project_foler, f)
200
+ if os.path.exists(fp):
201
+ # e.g., \input{srcs/07_appendix.tex}
202
+ with open(fp, 'r', encoding='utf-8', errors='replace') as fx:
203
+ c = fx.read()
204
+ else:
205
+ # e.g., \input{srcs/07_appendix}
206
+ with open(fp+'.tex', 'r', encoding='utf-8', errors='replace') as fx:
207
+ c = fx.read()
208
+ c = merge_tex_files_(project_foler, c, mode)
209
+ main_file = main_file[:s.span()[0]] + c + main_file[s.span()[1]:]
210
+ return main_file
211
+
212
+ def merge_tex_files(project_foler, main_file, mode):
213
+ """
214
+ Merge Tex project recrusively
215
+ P.S. 顺便把CTEX塞进去以支持中文
216
+ P.S. 顺便把Latex的注释去除
217
+ """
218
+ main_file = merge_tex_files_(project_foler, main_file, mode)
219
+ main_file = rm_comments(main_file)
220
+
221
+ if mode == 'translate_zh':
222
+ # find paper documentclass
223
+ pattern = re.compile(r'\\documentclass.*\n')
224
+ match = pattern.search(main_file)
225
+ assert match is not None, "Cannot find documentclass statement!"
226
+ position = match.end()
227
+ add_ctex = '\\usepackage{ctex}\n'
228
+ add_url = '\\usepackage{url}\n' if '{url}' not in main_file else ''
229
+ main_file = main_file[:position] + add_ctex + add_url + main_file[position:]
230
+ # fontset=windows
231
+ import platform
232
+ main_file = re.sub(r"\\documentclass\[(.*?)\]{(.*?)}", r"\\documentclass[\1,fontset=windows,UTF8]{\2}",main_file)
233
+ main_file = re.sub(r"\\documentclass{(.*?)}", r"\\documentclass[fontset=windows,UTF8]{\1}",main_file)
234
+ # find paper abstract
235
+ pattern_opt1 = re.compile(r'\\begin\{abstract\}.*\n')
236
+ pattern_opt2 = re.compile(r"\\abstract\{(.*?)\}", flags=re.DOTALL)
237
+ match_opt1 = pattern_opt1.search(main_file)
238
+ match_opt2 = pattern_opt2.search(main_file)
239
+ assert (match_opt1 is not None) or (match_opt2 is not None), "Cannot find paper abstract section!"
240
+ return main_file
241
+
242
+
243
+
244
+ """
245
+ ========================================================================
246
+ Post process
247
+ ========================================================================
248
+ """
249
+ def mod_inbraket(match):
250
+ """
251
+ 为啥chatgpt会把cite里面的逗号换成中文逗号呀
252
+ """
253
+ # get the matched string
254
+ cmd = match.group(1)
255
+ str_to_modify = match.group(2)
256
+ # modify the matched string
257
+ str_to_modify = str_to_modify.replace(':', ':') # 前面是中文冒号,后面是英文冒号
258
+ str_to_modify = str_to_modify.replace(',', ',') # 前面是中文逗号,后面是英文逗号
259
+ # str_to_modify = 'BOOM'
260
+ return "\\" + cmd + "{" + str_to_modify + "}"
261
+
262
+ def fix_content(final_tex, node_string):
263
+ """
264
+ Fix common GPT errors to increase success rate
265
+ """
266
+ final_tex = re.sub(r"(?<!\\)%", "\\%", final_tex)
267
+ final_tex = re.sub(r"\\([a-z]{2,10})\ \{", r"\\\1{", string=final_tex)
268
+ final_tex = re.sub(r"\\\ ([a-z]{2,10})\{", r"\\\1{", string=final_tex)
269
+ final_tex = re.sub(r"\\([a-z]{2,10})\{([^\}]*?)\}", mod_inbraket, string=final_tex)
270
+
271
+ if "Traceback" in final_tex and "[Local Message]" in final_tex:
272
+ final_tex = node_string # 出问题了,还原原文
273
+ if node_string.count('\\begin') != final_tex.count('\\begin'):
274
+ final_tex = node_string # 出问题了,还原原文
275
+ if node_string.count('\_') > 0 and node_string.count('\_') > final_tex.count('\_'):
276
+ # walk and replace any _ without \
277
+ final_tex = re.sub(r"(?<!\\)_", "\\_", final_tex)
278
+
279
+ def compute_brace_level(string):
280
+ # this function count the number of { and }
281
+ brace_level = 0
282
+ for c in string:
283
+ if c == "{": brace_level += 1
284
+ elif c == "}": brace_level -= 1
285
+ return brace_level
286
+ def join_most(tex_t, tex_o):
287
+ # this function join translated string and original string when something goes wrong
288
+ p_t = 0
289
+ p_o = 0
290
+ def find_next(string, chars, begin):
291
+ p = begin
292
+ while p < len(string):
293
+ if string[p] in chars: return p, string[p]
294
+ p += 1
295
+ return None, None
296
+ while True:
297
+ res1, char = find_next(tex_o, ['{','}'], p_o)
298
+ if res1 is None: break
299
+ res2, char = find_next(tex_t, [char], p_t)
300
+ if res2 is None: break
301
+ p_o = res1 + 1
302
+ p_t = res2 + 1
303
+ return tex_t[:p_t] + tex_o[p_o:]
304
+
305
+ if compute_brace_level(final_tex) != compute_brace_level(node_string):
306
+ # 出问题了,还原部分原文,保证括号正确
307
+ final_tex = join_most(final_tex, node_string)
308
+ return final_tex
309
+
310
+ def split_subprocess(txt, project_folder, return_dict, opts):
311
+ """
312
+ break down latex file to a linked list,
313
+ each node use a preserve flag to indicate whether it should
314
+ be proccessed by GPT.
315
+ """
316
+ text = txt
317
+ mask = np.zeros(len(txt), dtype=np.uint8) + TRANSFORM
318
+
319
+ # 吸收title与作者以上的部分
320
+ text, mask = set_forbidden_text(text, mask, r"(.*?)\\maketitle", re.DOTALL)
321
+ # 吸收iffalse注释
322
+ text, mask = set_forbidden_text(text, mask, r"\\iffalse(.*?)\\fi", re.DOTALL)
323
+ # 吸收在42行以内的begin-end组合
324
+ text, mask = set_forbidden_text_begin_end(text, mask, r"\\begin\{([a-z\*]*)\}(.*?)\\end\{\1\}", re.DOTALL, limit_n_lines=42)
325
+ # 吸收匿名公式
326
+ text, mask = set_forbidden_text(text, mask, [ r"\$\$(.*?)\$\$", r"\\\[.*?\\\]" ], re.DOTALL)
327
+ # 吸收其他杂项
328
+ text, mask = set_forbidden_text(text, mask, [ r"\\section\{(.*?)\}", r"\\section\*\{(.*?)\}", r"\\subsection\{(.*?)\}", r"\\subsubsection\{(.*?)\}" ])
329
+ text, mask = set_forbidden_text(text, mask, [ r"\\bibliography\{(.*?)\}", r"\\bibliographystyle\{(.*?)\}" ])
330
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{thebibliography\}.*?\\end\{thebibliography\}", re.DOTALL)
331
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{lstlisting\}(.*?)\\end\{lstlisting\}", re.DOTALL)
332
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{wraptable\}(.*?)\\end\{wraptable\}", re.DOTALL)
333
+ text, mask = set_forbidden_text(text, mask, r"\\begin\{algorithm\}(.*?)\\end\{algorithm\}", re.DOTALL)
334
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{wrapfigure\}(.*?)\\end\{wrapfigure\}", r"\\begin\{wrapfigure\*\}(.*?)\\end\{wrapfigure\*\}"], re.DOTALL)
335
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{figure\}(.*?)\\end\{figure\}", r"\\begin\{figure\*\}(.*?)\\end\{figure\*\}"], re.DOTALL)
336
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{multline\}(.*?)\\end\{multline\}", r"\\begin\{multline\*\}(.*?)\\end\{multline\*\}"], re.DOTALL)
337
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{table\}(.*?)\\end\{table\}", r"\\begin\{table\*\}(.*?)\\end\{table\*\}"], re.DOTALL)
338
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{minipage\}(.*?)\\end\{minipage\}", r"\\begin\{minipage\*\}(.*?)\\end\{minipage\*\}"], re.DOTALL)
339
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{align\*\}(.*?)\\end\{align\*\}", r"\\begin\{align\}(.*?)\\end\{align\}"], re.DOTALL)
340
+ text, mask = set_forbidden_text(text, mask, [r"\\begin\{equation\}(.*?)\\end\{equation\}", r"\\begin\{equation\*\}(.*?)\\end\{equation\*\}"], re.DOTALL)
341
+ text, mask = set_forbidden_text(text, mask, [r"\\includepdf\[(.*?)\]\{(.*?)\}", r"\\clearpage", r"\\newpage", r"\\appendix", r"\\tableofcontents", r"\\include\{(.*?)\}"])
342
+ text, mask = set_forbidden_text(text, mask, [r"\\vspace\{(.*?)\}", r"\\hspace\{(.*?)\}", r"\\label\{(.*?)\}", r"\\begin\{(.*?)\}", r"\\end\{(.*?)\}", r"\\item "])
343
+ text, mask = set_forbidden_text_careful_brace(text, mask, r"\\hl\{(.*?)\}", re.DOTALL)
344
+ # reverse 操作必须放在最后
345
+ text, mask = reverse_forbidden_text_careful_brace(text, mask, r"\\caption\{(.*?)\}", re.DOTALL, forbid_wrapper=True)
346
+ text, mask = reverse_forbidden_text_careful_brace(text, mask, r"\\abstract\{(.*?)\}", re.DOTALL, forbid_wrapper=True)
347
+ text, mask = reverse_forbidden_text(text, mask, r"\\begin\{abstract\}(.*?)\\end\{abstract\}", re.DOTALL, forbid_wrapper=True)
348
+ root = convert_to_linklist(text, mask)
349
+
350
+ # 修复括号
351
+ node = root
352
+ while True:
353
+ string = node.string
354
+ if node.preserve:
355
+ node = node.next
356
+ if node is None: break
357
+ continue
358
+ def break_check(string):
359
+ str_stack = [""] # (lv, index)
360
+ for i, c in enumerate(string):
361
+ if c == '{':
362
+ str_stack.append('{')
363
+ elif c == '}':
364
+ if len(str_stack) == 1:
365
+ print('stack fix')
366
+ return i
367
+ str_stack.pop(-1)
368
+ else:
369
+ str_stack[-1] += c
370
+ return -1
371
+ bp = break_check(string)
372
+
373
+ if bp == -1:
374
+ pass
375
+ elif bp == 0:
376
+ node.string = string[:1]
377
+ q = LinkedListNode(string[1:], False)
378
+ q.next = node.next
379
+ node.next = q
380
+ else:
381
+ node.string = string[:bp]
382
+ q = LinkedListNode(string[bp:], False)
383
+ q.next = node.next
384
+ node.next = q
385
+
386
+ node = node.next
387
+ if node is None: break
388
+
389
+ # 屏蔽空行和太短的句子
390
+ node = root
391
+ while True:
392
+ if len(node.string.strip('\n').strip(''))==0: node.preserve = True
393
+ if len(node.string.strip('\n').strip(''))<42: node.preserve = True
394
+ node = node.next
395
+ if node is None: break
396
+ node = root
397
+ while True:
398
+ if node.next and node.preserve and node.next.preserve:
399
+ node.string += node.next.string
400
+ node.next = node.next.next
401
+ node = node.next
402
+ if node is None: break
403
+
404
+ # 将前后断行符脱离
405
+ node = root
406
+ prev_node = None
407
+ while True:
408
+ if not node.preserve:
409
+ lstriped_ = node.string.lstrip().lstrip('\n')
410
+ if (prev_node is not None) and (prev_node.preserve) and (len(lstriped_)!=len(node.string)):
411
+ prev_node.string += node.string[:-len(lstriped_)]
412
+ node.string = lstriped_
413
+ rstriped_ = node.string.rstrip().rstrip('\n')
414
+ if (node.next is not None) and (node.next.preserve) and (len(rstriped_)!=len(node.string)):
415
+ node.next.string = node.string[len(rstriped_):] + node.next.string
416
+ node.string = rstriped_
417
+ # =====
418
+ prev_node = node
419
+ node = node.next
420
+ if node is None: break
421
+ # 输出html调试文件,用红色标注处保留区(PRESERVE),用黑色标注转换区(TRANSFORM)
422
+ with open(pj(project_folder, 'debug_log.html'), 'w', encoding='utf8') as f:
423
+ segment_parts_for_gpt = []
424
+ nodes = []
425
+ node = root
426
+ while True:
427
+ nodes.append(node)
428
+ show_html = node.string.replace('\n','<br/>')
429
+ if not node.preserve:
430
+ segment_parts_for_gpt.append(node.string)
431
+ f.write(f'<p style="color:black;">#{show_html}#</p>')
432
+ else:
433
+ f.write(f'<p style="color:red;">{show_html}</p>')
434
+ node = node.next
435
+ if node is None: break
436
+
437
+ for n in nodes: n.next = None # break
438
+ return_dict['nodes'] = nodes
439
+ return_dict['segment_parts_for_gpt'] = segment_parts_for_gpt
440
+ return return_dict
441
+
442
+
443
+
444
+ class LatexPaperSplit():
445
+ """
446
+ break down latex file to a linked list,
447
+ each node use a preserve flag to indicate whether it should
448
+ be proccessed by GPT.
449
+ """
450
+ def __init__(self) -> None:
451
+ self.nodes = None
452
+ self.msg = "*{\\scriptsize\\textbf{警告:该PDF由GPT-Academic开源项目调用大语言模型+Latex翻译插件一键生成," + \
453
+ "版权归原文作者所有。翻译内容可靠性无保障,请仔细鉴别并以原文为准。" + \
454
+ "项目Github地址 \\url{https://github.com/binary-husky/gpt_academic/}。"
455
+ # 请您不要删除或修改这行警告,除非您是论文的原作者(如果您是论文原作者,欢迎加REAME中的QQ联系开发者)
456
+ self.msg_declare = "为了防止大语言模型的意外谬误产生扩散影响,禁止移除或修改此警告。}}\\\\"
457
+
458
+ def merge_result(self, arr, mode, msg):
459
+ """
460
+ Merge the result after the GPT process completed
461
+ """
462
+ result_string = ""
463
+ p = 0
464
+ for node in self.nodes:
465
+ if node.preserve:
466
+ result_string += node.string
467
+ else:
468
+ result_string += fix_content(arr[p], node.string)
469
+ p += 1
470
+ if mode == 'translate_zh':
471
+ pattern = re.compile(r'\\begin\{abstract\}.*\n')
472
+ match = pattern.search(result_string)
473
+ if not match:
474
+ # match \abstract{xxxx}
475
+ pattern_compile = re.compile(r"\\abstract\{(.*?)\}", flags=re.DOTALL)
476
+ match = pattern_compile.search(result_string)
477
+ position = match.regs[1][0]
478
+ else:
479
+ # match \begin{abstract}xxxx\end{abstract}
480
+ position = match.end()
481
+ result_string = result_string[:position] + self.msg + msg + self.msg_declare + result_string[position:]
482
+ return result_string
483
+
484
+ def split(self, txt, project_folder, opts):
485
+ """
486
+ break down latex file to a linked list,
487
+ each node use a preserve flag to indicate whether it should
488
+ be proccessed by GPT.
489
+ P.S. use multiprocessing to avoid timeout error
490
+ """
491
+ import multiprocessing
492
+ manager = multiprocessing.Manager()
493
+ return_dict = manager.dict()
494
+ p = multiprocessing.Process(
495
+ target=split_subprocess,
496
+ args=(txt, project_folder, return_dict, opts))
497
+ p.start()
498
+ p.join()
499
+ p.close()
500
+ self.nodes = return_dict['nodes']
501
+ self.sp = return_dict['segment_parts_for_gpt']
502
+ return self.sp
503
+
504
+
505
+
506
+ class LatexPaperFileGroup():
507
+ """
508
+ use tokenizer to break down text according to max_token_limit
509
+ """
510
+ def __init__(self):
511
+ self.file_paths = []
512
+ self.file_contents = []
513
+ self.sp_file_contents = []
514
+ self.sp_file_index = []
515
+ self.sp_file_tag = []
516
+
517
+ # count_token
518
+ from request_llm.bridge_all import model_info
519
+ enc = model_info["gpt-3.5-turbo"]['tokenizer']
520
+ def get_token_num(txt): return len(enc.encode(txt, disallowed_special=()))
521
+ self.get_token_num = get_token_num
522
+
523
+ def run_file_split(self, max_token_limit=1900):
524
+ """
525
+ use tokenizer to break down text according to max_token_limit
526
+ """
527
+ for index, file_content in enumerate(self.file_contents):
528
+ if self.get_token_num(file_content) < max_token_limit:
529
+ self.sp_file_contents.append(file_content)
530
+ self.sp_file_index.append(index)
531
+ self.sp_file_tag.append(self.file_paths[index])
532
+ else:
533
+ from .crazy_utils import breakdown_txt_to_satisfy_token_limit_for_pdf
534
+ segments = breakdown_txt_to_satisfy_token_limit_for_pdf(file_content, self.get_token_num, max_token_limit)
535
+ for j, segment in enumerate(segments):
536
+ self.sp_file_contents.append(segment)
537
+ self.sp_file_index.append(index)
538
+ self.sp_file_tag.append(self.file_paths[index] + f".part-{j}.tex")
539
+ print('Segmentation: done')
540
+
541
+ def merge_result(self):
542
+ self.file_result = ["" for _ in range(len(self.file_paths))]
543
+ for r, k in zip(self.sp_file_result, self.sp_file_index):
544
+ self.file_result[k] += r
545
+
546
+ def write_result(self):
547
+ manifest = []
548
+ for path, res in zip(self.file_paths, self.file_result):
549
+ with open(path + '.polish.tex', 'w', encoding='utf8') as f:
550
+ manifest.append(path + '.polish.tex')
551
+ f.write(res)
552
+ return manifest
553
+
554
+ def write_html(sp_file_contents, sp_file_result, chatbot, project_folder):
555
+
556
+ # write html
557
+ try:
558
+ import shutil
559
+ from .crazy_utils import construct_html
560
+ from toolbox import gen_time_str
561
+ ch = construct_html()
562
+ orig = ""
563
+ trans = ""
564
+ final = []
565
+ for c,r in zip(sp_file_contents, sp_file_result):
566
+ final.append(c)
567
+ final.append(r)
568
+ for i, k in enumerate(final):
569
+ if i%2==0:
570
+ orig = k
571
+ if i%2==1:
572
+ trans = k
573
+ ch.add_row(a=orig, b=trans)
574
+ create_report_file_name = f"{gen_time_str()}.trans.html"
575
+ ch.save_file(create_report_file_name)
576
+ shutil.copyfile(pj('./gpt_log/', create_report_file_name), pj(project_folder, create_report_file_name))
577
+ promote_file_to_downloadzone(file=f'./gpt_log/{create_report_file_name}', chatbot=chatbot)
578
+ except:
579
+ from toolbox import trimmed_format_exc
580
+ print('writing html result failed:', trimmed_format_exc())
581
+
582
+ def Latex精细分解与转化(file_manifest, project_folder, llm_kwargs, plugin_kwargs, chatbot, history, system_prompt, mode='proofread', switch_prompt=None, opts=[]):
583
+ import time, os, re
584
+ from .crazy_utils import request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency
585
+ from .latex_utils import LatexPaperFileGroup, merge_tex_files, LatexPaperSplit, 寻找Latex主文件
586
+
587
+ # <-------- 寻找主tex文件 ---------->
588
+ maintex = 寻找Latex主文件(file_manifest, mode)
589
+ chatbot.append((f"定位主Latex文件", f'[Local Message] 分析结果:该项目的Latex主文件是{maintex}, 如果分析错误, 请立即终止程序, 删除或修改歧义文件, 然后重试。主程序即将开始, 请稍候。'))
590
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
591
+ time.sleep(3)
592
+
593
+ # <-------- 读取Latex文件, 将多文件tex工程融合为一个巨型tex ---------->
594
+ main_tex_basename = os.path.basename(maintex)
595
+ assert main_tex_basename.endswith('.tex')
596
+ main_tex_basename_bare = main_tex_basename[:-4]
597
+ may_exist_bbl = pj(project_folder, f'{main_tex_basename_bare}.bbl')
598
+ if os.path.exists(may_exist_bbl):
599
+ shutil.copyfile(may_exist_bbl, pj(project_folder, f'merge.bbl'))
600
+ shutil.copyfile(may_exist_bbl, pj(project_folder, f'merge_{mode}.bbl'))
601
+ shutil.copyfile(may_exist_bbl, pj(project_folder, f'merge_diff.bbl'))
602
+
603
+ with open(maintex, 'r', encoding='utf-8', errors='replace') as f:
604
+ content = f.read()
605
+ merged_content = merge_tex_files(project_folder, content, mode)
606
+
607
+ with open(project_folder + '/merge.tex', 'w', encoding='utf-8', errors='replace') as f:
608
+ f.write(merged_content)
609
+
610
+ # <-------- 精细切分latex文件 ---------->
611
+ chatbot.append((f"Latex文件融合完成", f'[Local Message] 正在精细切分latex文件,这需要一段时间计算,文档越长耗时越长,请耐心等待。'))
612
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
613
+ lps = LatexPaperSplit()
614
+ res = lps.split(merged_content, project_folder, opts) # 消耗时间的函数
615
+
616
+ # <-------- 拆分过长的latex片段 ---------->
617
+ pfg = LatexPaperFileGroup()
618
+ for index, r in enumerate(res):
619
+ pfg.file_paths.append('segment-' + str(index))
620
+ pfg.file_contents.append(r)
621
+
622
+ pfg.run_file_split(max_token_limit=1024)
623
+ n_split = len(pfg.sp_file_contents)
624
+
625
+ # <-------- 根据需要切换prompt ---------->
626
+ inputs_array, sys_prompt_array = switch_prompt(pfg, mode)
627
+ inputs_show_user_array = [f"{mode} {f}" for f in pfg.sp_file_tag]
628
+
629
+ if os.path.exists(pj(project_folder,'temp.pkl')):
630
+
631
+ # <-------- 【仅调试】如果存在调试缓存文件,则跳过GPT请求环节 ---------->
632
+ pfg = objload(file=pj(project_folder,'temp.pkl'))
633
+
634
+ else:
635
+ # <-------- gpt 多线程请求 ---------->
636
+ gpt_response_collection = yield from request_gpt_model_multi_threads_with_very_awesome_ui_and_high_efficiency(
637
+ inputs_array=inputs_array,
638
+ inputs_show_user_array=inputs_show_user_array,
639
+ llm_kwargs=llm_kwargs,
640
+ chatbot=chatbot,
641
+ history_array=[[""] for _ in range(n_split)],
642
+ sys_prompt_array=sys_prompt_array,
643
+ # max_workers=5, # 并行任务数量限制, 最多同时执行5个, 其他的排队等待
644
+ scroller_max_len = 40
645
+ )
646
+
647
+ # <-------- 文本碎片重组为完整的tex片段 ---------->
648
+ pfg.sp_file_result = []
649
+ for i_say, gpt_say, orig_content in zip(gpt_response_collection[0::2], gpt_response_collection[1::2], pfg.sp_file_contents):
650
+ pfg.sp_file_result.append(gpt_say)
651
+ pfg.merge_result()
652
+
653
+ # <-------- 临时存储用于调试 ---------->
654
+ pfg.get_token_num = None
655
+ objdump(pfg, file=pj(project_folder,'temp.pkl'))
656
+
657
+ write_html(pfg.sp_file_contents, pfg.sp_file_result, chatbot=chatbot, project_folder=project_folder)
658
+
659
+ # <-------- 写出文件 ---------->
660
+ msg = f"当前大语言模型: {llm_kwargs['llm_model']},当前语言模型温度设定: {llm_kwargs['temperature']}。"
661
+ final_tex = lps.merge_result(pfg.file_result, mode, msg)
662
+ with open(project_folder + f'/merge_{mode}.tex', 'w', encoding='utf-8', errors='replace') as f:
663
+ if mode != 'translate_zh' or "binary" in final_tex: f.write(final_tex)
664
+
665
+
666
+ # <-------- 整理结果, 退出 ---------->
667
+ chatbot.append((f"完成了吗?", 'GPT结果已输出, 正在编译PDF'))
668
+ yield from update_ui(chatbot=chatbot, history=history) # 刷新界面
669
+
670
+ # <-------- 返回 ---------->
671
+ return project_folder + f'/merge_{mode}.tex'
672
+
673
+
674
+
675
+ def remove_buggy_lines(file_path, log_path, tex_name, tex_name_pure, n_fix, work_folder_modified):
676
+ try:
677
+ with open(log_path, 'r', encoding='utf-8', errors='replace') as f:
678
+ log = f.read()
679
+ with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
680
+ file_lines = f.readlines()
681
+ import re
682
+ buggy_lines = re.findall(tex_name+':([0-9]{1,5}):', log)
683
+ buggy_lines = [int(l) for l in buggy_lines]
684
+ buggy_lines = sorted(buggy_lines)
685
+ print("removing lines that has errors", buggy_lines)
686
+ file_lines.pop(buggy_lines[0]-1)
687
+ with open(pj(work_folder_modified, f"{tex_name_pure}_fix_{n_fix}.tex"), 'w', encoding='utf-8', errors='replace') as f:
688
+ f.writelines(file_lines)
689
+ return True, f"{tex_name_pure}_fix_{n_fix}", buggy_lines
690
+ except:
691
+ print("Fatal error occurred, but we cannot identify error, please download zip, read latex log, and compile manually.")
692
+ return False, -1, [-1]
693
+
694
+ def compile_latex_with_timeout(command, cwd, timeout=60):
695
+ import subprocess
696
+ process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)
697
+ try:
698
+ stdout, stderr = process.communicate(timeout=timeout)
699
+ except subprocess.TimeoutExpired:
700
+ process.kill()
701
+ stdout, stderr = process.communicate()
702
+ print("Process timed out!")
703
+ return False
704
+ return True
705
+
706
+ def 编译Latex(chatbot, history, main_file_original, main_file_modified, work_folder_original, work_folder_modified, work_folder, mode='default'):
707
+ import os, time
708
+ current_dir = os.getcwd()
709
+ n_fix = 1
710
+ max_try = 32
711
+ chatbot.append([f"正在编译PDF文档", f'编译已经开始。当前工作路径为{work_folder},如果程序停顿5分钟以上,请直接去该路径下取回翻译结果,或者重启之后再度尝试 ...']); yield from update_ui(chatbot=chatbot, history=history)
712
+ chatbot.append([f"正在编译PDF文档", '...']); yield from update_ui(chatbot=chatbot, history=history); time.sleep(1); chatbot[-1] = list(chatbot[-1]) # 刷新界面
713
+ yield from update_ui_lastest_msg('编译已经开始...', chatbot, history) # 刷新Gradio前端界面
714
+
715
+ while True:
716
+ import os
717
+
718
+ # https://stackoverflow.com/questions/738755/dont-make-me-manually-abort-a-latex-compile-when-theres-an-error
719
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译原始PDF ...', chatbot, history) # 刷新Gradio前端界面
720
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_original}.tex', work_folder_original)
721
+
722
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译转化后的PDF ...', chatbot, history) # 刷新Gradio前端界面
723
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_modified}.tex', work_folder_modified)
724
+
725
+ if ok and os.path.exists(pj(work_folder_modified, f'{main_file_modified}.pdf')):
726
+ # 只有第二步成功,才能继续下面的步骤
727
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译BibTex ...', chatbot, history) # 刷新Gradio前端界面
728
+ if not os.path.exists(pj(work_folder_original, f'{main_file_original}.bbl')):
729
+ ok = compile_latex_with_timeout(f'bibtex {main_file_original}.aux', work_folder_original)
730
+ if not os.path.exists(pj(work_folder_modified, f'{main_file_modified}.bbl')):
731
+ ok = compile_latex_with_timeout(f'bibtex {main_file_modified}.aux', work_folder_modified)
732
+
733
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 编译文献交叉引用 ...', chatbot, history) # 刷新Gradio前端界面
734
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_original}.tex', work_folder_original)
735
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_modified}.tex', work_folder_modified)
736
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_original}.tex', work_folder_original)
737
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error {main_file_modified}.tex', work_folder_modified)
738
+
739
+ if mode!='translate_zh':
740
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 使用latexdiff生成论文转化前后对比 ...', chatbot, history) # 刷新Gradio前端界面
741
+ print( f'latexdiff --encoding=utf8 --append-safecmd=subfile {work_folder_original}/{main_file_original}.tex {work_folder_modified}/{main_file_modified}.tex --flatten > {work_folder}/merge_diff.tex')
742
+ ok = compile_latex_with_timeout(f'latexdiff --encoding=utf8 --append-safecmd=subfile {work_folder_original}/{main_file_original}.tex {work_folder_modified}/{main_file_modified}.tex --flatten > {work_folder}/merge_diff.tex')
743
+
744
+ yield from update_ui_lastest_msg(f'尝试第 {n_fix}/{max_try} 次编译, 正在编译对比PDF ...', chatbot, history) # 刷新Gradio前端界面
745
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
746
+ ok = compile_latex_with_timeout(f'bibtex merge_diff.aux', work_folder)
747
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
748
+ ok = compile_latex_with_timeout(f'pdflatex -interaction=batchmode -file-line-error merge_diff.tex', work_folder)
749
+
750
+
751
+ # <---------- 检查结果 ----------->
752
+ results_ = ""
753
+ original_pdf_success = os.path.exists(pj(work_folder_original, f'{main_file_original}.pdf'))
754
+ modified_pdf_success = os.path.exists(pj(work_folder_modified, f'{main_file_modified}.pdf'))
755
+ diff_pdf_success = os.path.exists(pj(work_folder, f'merge_diff.pdf'))
756
+ results_ += f"原始PDF编译是否成功: {original_pdf_success};"
757
+ results_ += f"转化PDF编译是否成功: {modified_pdf_success};"
758
+ results_ += f"对比PDF编译是否成功: {diff_pdf_success};"
759
+ yield from update_ui_lastest_msg(f'第{n_fix}编译结束:<br/>{results_}...', chatbot, history) # 刷新Gradio前端界面
760
+
761
+ if diff_pdf_success:
762
+ result_pdf = pj(work_folder_modified, f'merge_diff.pdf') # get pdf path
763
+ promote_file_to_downloadzone(result_pdf, rename_file=None, chatbot=chatbot) # promote file to web UI
764
+ if modified_pdf_success:
765
+ yield from update_ui_lastest_msg(f'转化PDF编译已经成功, 即将退出 ...', chatbot, history) # 刷新Gradio前端界面
766
+ result_pdf = pj(work_folder_modified, f'{main_file_modified}.pdf') # get pdf path
767
+ if os.path.exists(pj(work_folder, '..', 'translation')):
768
+ shutil.copyfile(result_pdf, pj(work_folder, '..', 'translation', 'translate_zh.pdf'))
769
+ promote_file_to_downloadzone(result_pdf, rename_file=None, chatbot=chatbot) # promote file to web UI
770
+ return True # 成功啦
771
+ else:
772
+ if n_fix>=max_try: break
773
+ n_fix += 1
774
+ can_retry, main_file_modified, buggy_lines = remove_buggy_lines(
775
+ file_path=pj(work_folder_modified, f'{main_file_modified}.tex'),
776
+ log_path=pj(work_folder_modified, f'{main_file_modified}.log'),
777
+ tex_name=f'{main_file_modified}.tex',
778
+ tex_name_pure=f'{main_file_modified}',
779
+ n_fix=n_fix,
780
+ work_folder_modified=work_folder_modified,
781
+ )
782
+ yield from update_ui_lastest_msg(f'由于最为关键的转化PDF编译失败, 将根据报错信息修正tex源文件并重试, 当前报错的latex代码处于第{buggy_lines}行 ...', chatbot, history) # 刷新Gradio前端界面
783
+ if not can_retry: break
784
+
785
+ return False # 失败啦
786
+
787
+
788
+
crazy_functions/live_audio/aliyunASR.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time, logging, json
2
+
3
+
4
+ class AliyunASR():
5
+
6
+ def test_on_sentence_begin(self, message, *args):
7
+ # print("test_on_sentence_begin:{}".format(message))
8
+ pass
9
+
10
+ def test_on_sentence_end(self, message, *args):
11
+ # print("test_on_sentence_end:{}".format(message))
12
+ message = json.loads(message)
13
+ self.parsed_sentence = message['payload']['result']
14
+ self.event_on_entence_end.set()
15
+ # print(self.parsed_sentence)
16
+
17
+ def test_on_start(self, message, *args):
18
+ # print("test_on_start:{}".format(message))
19
+ pass
20
+
21
+ def test_on_error(self, message, *args):
22
+ logging.error("on_error args=>{}".format(args))
23
+ pass
24
+
25
+ def test_on_close(self, *args):
26
+ self.aliyun_service_ok = False
27
+ pass
28
+
29
+ def test_on_result_chg(self, message, *args):
30
+ # print("test_on_chg:{}".format(message))
31
+ message = json.loads(message)
32
+ self.parsed_text = message['payload']['result']
33
+ self.event_on_result_chg.set()
34
+
35
+ def test_on_completed(self, message, *args):
36
+ # print("on_completed:args=>{} message=>{}".format(args, message))
37
+ pass
38
+
39
+ def audio_convertion_thread(self, uuid):
40
+ # 在一个异步线程中采集音频
41
+ import nls # pip install git+https://github.com/aliyun/alibabacloud-nls-python-sdk.git
42
+ import tempfile
43
+ from scipy import io
44
+ from toolbox import get_conf
45
+ from .audio_io import change_sample_rate
46
+ from .audio_io import RealtimeAudioDistribution
47
+ NEW_SAMPLERATE = 16000
48
+ rad = RealtimeAudioDistribution()
49
+ rad.clean_up()
50
+ temp_folder = tempfile.gettempdir()
51
+ TOKEN, APPKEY = get_conf('ALIYUN_TOKEN', 'ALIYUN_APPKEY')
52
+ if len(TOKEN) == 0:
53
+ TOKEN = self.get_token()
54
+ self.aliyun_service_ok = True
55
+ URL="wss://nls-gateway.aliyuncs.com/ws/v1"
56
+ sr = nls.NlsSpeechTranscriber(
57
+ url=URL,
58
+ token=TOKEN,
59
+ appkey=APPKEY,
60
+ on_sentence_begin=self.test_on_sentence_begin,
61
+ on_sentence_end=self.test_on_sentence_end,
62
+ on_start=self.test_on_start,
63
+ on_result_changed=self.test_on_result_chg,
64
+ on_completed=self.test_on_completed,
65
+ on_error=self.test_on_error,
66
+ on_close=self.test_on_close,
67
+ callback_args=[uuid.hex]
68
+ )
69
+
70
+ r = sr.start(aformat="pcm",
71
+ enable_intermediate_result=True,
72
+ enable_punctuation_prediction=True,
73
+ enable_inverse_text_normalization=True)
74
+
75
+ while not self.stop:
76
+ # time.sleep(self.capture_interval)
77
+ audio = rad.read(uuid.hex)
78
+ if audio is not None:
79
+ # convert to pcm file
80
+ temp_file = f'{temp_folder}/{uuid.hex}.pcm' #
81
+ dsdata = change_sample_rate(audio, rad.rate, NEW_SAMPLERATE) # 48000 --> 16000
82
+ io.wavfile.write(temp_file, NEW_SAMPLERATE, dsdata)
83
+ # read pcm binary
84
+ with open(temp_file, "rb") as f: data = f.read()
85
+ # print('audio len:', len(audio), '\t ds len:', len(dsdata), '\t need n send:', len(data)//640)
86
+ slices = zip(*(iter(data),) * 640) # 640个字节为一组
87
+ for i in slices: sr.send_audio(bytes(i))
88
+ else:
89
+ time.sleep(0.1)
90
+
91
+ if not self.aliyun_service_ok:
92
+ self.stop = True
93
+ self.stop_msg = 'Aliyun音频服务异常,请检查ALIYUN_TOKEN和ALIYUN_APPKEY是否过期。'
94
+ r = sr.stop()
95
+
96
+ def get_token(self):
97
+ from toolbox import get_conf
98
+ import json
99
+ from aliyunsdkcore.request import CommonRequest
100
+ from aliyunsdkcore.client import AcsClient
101
+ AccessKey_ID, AccessKey_secret = get_conf('ALIYUN_ACCESSKEY', 'ALIYUN_SECRET')
102
+
103
+ # 创建AcsClient实例
104
+ client = AcsClient(
105
+ AccessKey_ID,
106
+ AccessKey_secret,
107
+ "cn-shanghai"
108
+ )
109
+
110
+ # 创建request,并设置参数。
111
+ request = CommonRequest()
112
+ request.set_method('POST')
113
+ request.set_domain('nls-meta.cn-shanghai.aliyuncs.com')
114
+ request.set_version('2019-02-28')
115
+ request.set_action_name('CreateToken')
116
+
117
+ try:
118
+ response = client.do_action_with_exception(request)
119
+ print(response)
120
+ jss = json.loads(response)
121
+ if 'Token' in jss and 'Id' in jss['Token']:
122
+ token = jss['Token']['Id']
123
+ expireTime = jss['Token']['ExpireTime']
124
+ print("token = " + token)
125
+ print("expireTime = " + str(expireTime))
126
+ except Exception as e:
127
+ print(e)
128
+
129
+ return token
crazy_functions/live_audio/audio_io.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from scipy import interpolate
3
+
4
+ def Singleton(cls):
5
+ _instance = {}
6
+
7
+ def _singleton(*args, **kargs):
8
+ if cls not in _instance:
9
+ _instance[cls] = cls(*args, **kargs)
10
+ return _instance[cls]
11
+
12
+ return _singleton
13
+
14
+
15
+ @Singleton
16
+ class RealtimeAudioDistribution():
17
+ def __init__(self) -> None:
18
+ self.data = {}
19
+ self.max_len = 1024*1024
20
+ self.rate = 48000 # 只读,每秒采样数量
21
+
22
+ def clean_up(self):
23
+ self.data = {}
24
+
25
+ def feed(self, uuid, audio):
26
+ self.rate, audio_ = audio
27
+ # print('feed', len(audio_), audio_[-25:])
28
+ if uuid not in self.data:
29
+ self.data[uuid] = audio_
30
+ else:
31
+ new_arr = np.concatenate((self.data[uuid], audio_))
32
+ if len(new_arr) > self.max_len: new_arr = new_arr[-self.max_len:]
33
+ self.data[uuid] = new_arr
34
+
35
+ def read(self, uuid):
36
+ if uuid in self.data:
37
+ res = self.data.pop(uuid)
38
+ print('\r read-', len(res), '-', max(res), end='', flush=True)
39
+ else:
40
+ res = None
41
+ return res
42
+
43
+ def change_sample_rate(audio, old_sr, new_sr):
44
+ duration = audio.shape[0] / old_sr
45
+
46
+ time_old = np.linspace(0, duration, audio.shape[0])
47
+ time_new = np.linspace(0, duration, int(audio.shape[0] * new_sr / old_sr))
48
+
49
+ interpolator = interpolate.interp1d(time_old, audio.T)
50
+ new_audio = interpolator(time_new).T
51
+ return new_audio.astype(np.int16)
crazy_functions/pdf_fns/parse_pdf.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import random
3
+ from functools import lru_cache
4
+ class GROBID_OFFLINE_EXCEPTION(Exception): pass
5
+
6
+ def get_avail_grobid_url():
7
+ from toolbox import get_conf
8
+ GROBID_URLS, = get_conf('GROBID_URLS')
9
+ if len(GROBID_URLS) == 0: return None
10
+ try:
11
+ _grobid_url = random.choice(GROBID_URLS) # 随机负载均衡
12
+ if _grobid_url.endswith('/'): _grobid_url = _grobid_url.rstrip('/')
13
+ res = requests.get(_grobid_url+'/api/isalive')
14
+ if res.text=='true': return _grobid_url
15
+ else: return None
16
+ except:
17
+ return None
18
+
19
+ @lru_cache(maxsize=32)
20
+ def parse_pdf(pdf_path, grobid_url):
21
+ import scipdf # pip install scipdf_parser
22
+ if grobid_url.endswith('/'): grobid_url = grobid_url.rstrip('/')
23
+ try:
24
+ article_dict = scipdf.parse_pdf_to_dict(pdf_path, grobid_url=grobid_url)
25
+ except GROBID_OFFLINE_EXCEPTION:
26
+ raise GROBID_OFFLINE_EXCEPTION("GROBID服务不可用,请修改config中的GROBID_URL,可修改成本地GROBID服务。")
27
+ except:
28
+ raise RuntimeError("解析PDF失败,请检查PDF是否损坏。")
29
+ return article_dict
30
+
crazy_functions/test_project/cpp/cppipc/buffer.cpp ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "libipc/buffer.h"
2
+ #include "libipc/utility/pimpl.h"
3
+
4
+ #include <cstring>
5
+
6
+ namespace ipc {
7
+
8
+ bool operator==(buffer const & b1, buffer const & b2) {
9
+ return (b1.size() == b2.size()) && (std::memcmp(b1.data(), b2.data(), b1.size()) == 0);
10
+ }
11
+
12
+ bool operator!=(buffer const & b1, buffer const & b2) {
13
+ return !(b1 == b2);
14
+ }
15
+
16
+ class buffer::buffer_ : public pimpl<buffer_> {
17
+ public:
18
+ void* p_;
19
+ std::size_t s_;
20
+ void* a_;
21
+ buffer::destructor_t d_;
22
+
23
+ buffer_(void* p, std::size_t s, buffer::destructor_t d, void* a)
24
+ : p_(p), s_(s), a_(a), d_(d) {
25
+ }
26
+
27
+ ~buffer_() {
28
+ if (d_ == nullptr) return;
29
+ d_((a_ == nullptr) ? p_ : a_, s_);
30
+ }
31
+ };
32
+
33
+ buffer::buffer()
34
+ : buffer(nullptr, 0, nullptr, nullptr) {
35
+ }
36
+
37
+ buffer::buffer(void* p, std::size_t s, destructor_t d)
38
+ : p_(p_->make(p, s, d, nullptr)) {
39
+ }
40
+
41
+ buffer::buffer(void* p, std::size_t s, destructor_t d, void* additional)
42
+ : p_(p_->make(p, s, d, additional)) {
43
+ }
44
+
45
+ buffer::buffer(void* p, std::size_t s)
46
+ : buffer(p, s, nullptr) {
47
+ }
48
+
49
+ buffer::buffer(char const & c)
50
+ : buffer(const_cast<char*>(&c), 1) {
51
+ }
52
+
53
+ buffer::buffer(buffer&& rhs)
54
+ : buffer() {
55
+ swap(rhs);
56
+ }
57
+
58
+ buffer::~buffer() {
59
+ p_->clear();
60
+ }
61
+
62
+ void buffer::swap(buffer& rhs) {
63
+ std::swap(p_, rhs.p_);
64
+ }
65
+
66
+ buffer& buffer::operator=(buffer rhs) {
67
+ swap(rhs);
68
+ return *this;
69
+ }
70
+
71
+ bool buffer::empty() const noexcept {
72
+ return (impl(p_)->p_ == nullptr) || (impl(p_)->s_ == 0);
73
+ }
74
+
75
+ void* buffer::data() noexcept {
76
+ return impl(p_)->p_;
77
+ }
78
+
79
+ void const * buffer::data() const noexcept {
80
+ return impl(p_)->p_;
81
+ }
82
+
83
+ std::size_t buffer::size() const noexcept {
84
+ return impl(p_)->s_;
85
+ }
86
+
87
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/ipc.cpp ADDED
@@ -0,0 +1,701 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #include <type_traits>
3
+ #include <cstring>
4
+ #include <algorithm>
5
+ #include <utility> // std::pair, std::move, std::forward
6
+ #include <atomic>
7
+ #include <type_traits> // aligned_storage_t
8
+ #include <string>
9
+ #include <vector>
10
+ #include <array>
11
+ #include <cassert>
12
+
13
+ #include "libipc/ipc.h"
14
+ #include "libipc/def.h"
15
+ #include "libipc/shm.h"
16
+ #include "libipc/pool_alloc.h"
17
+ #include "libipc/queue.h"
18
+ #include "libipc/policy.h"
19
+ #include "libipc/rw_lock.h"
20
+ #include "libipc/waiter.h"
21
+
22
+ #include "libipc/utility/log.h"
23
+ #include "libipc/utility/id_pool.h"
24
+ #include "libipc/utility/scope_guard.h"
25
+ #include "libipc/utility/utility.h"
26
+
27
+ #include "libipc/memory/resource.h"
28
+ #include "libipc/platform/detail.h"
29
+ #include "libipc/circ/elem_array.h"
30
+
31
+ namespace {
32
+
33
+ using msg_id_t = std::uint32_t;
34
+ using acc_t = std::atomic<msg_id_t>;
35
+
36
+ template <std::size_t DataSize, std::size_t AlignSize>
37
+ struct msg_t;
38
+
39
+ template <std::size_t AlignSize>
40
+ struct msg_t<0, AlignSize> {
41
+ msg_id_t cc_id_;
42
+ msg_id_t id_;
43
+ std::int32_t remain_;
44
+ bool storage_;
45
+ };
46
+
47
+ template <std::size_t DataSize, std::size_t AlignSize>
48
+ struct msg_t : msg_t<0, AlignSize> {
49
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
50
+
51
+ msg_t() = default;
52
+ msg_t(msg_id_t cc_id, msg_id_t id, std::int32_t remain, void const * data, std::size_t size)
53
+ : msg_t<0, AlignSize> {cc_id, id, remain, (data == nullptr) || (size == 0)} {
54
+ if (this->storage_) {
55
+ if (data != nullptr) {
56
+ // copy storage-id
57
+ *reinterpret_cast<ipc::storage_id_t*>(&data_) =
58
+ *static_cast<ipc::storage_id_t const *>(data);
59
+ }
60
+ }
61
+ else std::memcpy(&data_, data, size);
62
+ }
63
+ };
64
+
65
+ template <typename T>
66
+ ipc::buff_t make_cache(T& data, std::size_t size) {
67
+ auto ptr = ipc::mem::alloc(size);
68
+ std::memcpy(ptr, &data, (ipc::detail::min)(sizeof(data), size));
69
+ return { ptr, size, ipc::mem::free };
70
+ }
71
+
72
+ struct cache_t {
73
+ std::size_t fill_;
74
+ ipc::buff_t buff_;
75
+
76
+ cache_t(std::size_t f, ipc::buff_t && b)
77
+ : fill_(f), buff_(std::move(b))
78
+ {}
79
+
80
+ void append(void const * data, std::size_t size) {
81
+ if (fill_ >= buff_.size() || data == nullptr || size == 0) return;
82
+ auto new_fill = (ipc::detail::min)(fill_ + size, buff_.size());
83
+ std::memcpy(static_cast<ipc::byte_t*>(buff_.data()) + fill_, data, new_fill - fill_);
84
+ fill_ = new_fill;
85
+ }
86
+ };
87
+
88
+ auto cc_acc() {
89
+ static ipc::shm::handle acc_h("__CA_CONN__", sizeof(acc_t));
90
+ return static_cast<acc_t*>(acc_h.get());
91
+ }
92
+
93
+ IPC_CONSTEXPR_ std::size_t align_chunk_size(std::size_t size) noexcept {
94
+ return (((size - 1) / ipc::large_msg_align) + 1) * ipc::large_msg_align;
95
+ }
96
+
97
+ IPC_CONSTEXPR_ std::size_t calc_chunk_size(std::size_t size) noexcept {
98
+ return ipc::make_align(alignof(std::max_align_t), align_chunk_size(
99
+ ipc::make_align(alignof(std::max_align_t), sizeof(std::atomic<ipc::circ::cc_t>)) + size));
100
+ }
101
+
102
+ struct chunk_t {
103
+ std::atomic<ipc::circ::cc_t> &conns() noexcept {
104
+ return *reinterpret_cast<std::atomic<ipc::circ::cc_t> *>(this);
105
+ }
106
+
107
+ void *data() noexcept {
108
+ return reinterpret_cast<ipc::byte_t *>(this)
109
+ + ipc::make_align(alignof(std::max_align_t), sizeof(std::atomic<ipc::circ::cc_t>));
110
+ }
111
+ };
112
+
113
+ struct chunk_info_t {
114
+ ipc::id_pool<> pool_;
115
+ ipc::spin_lock lock_;
116
+
117
+ IPC_CONSTEXPR_ static std::size_t chunks_mem_size(std::size_t chunk_size) noexcept {
118
+ return ipc::id_pool<>::max_count * chunk_size;
119
+ }
120
+
121
+ ipc::byte_t *chunks_mem() noexcept {
122
+ return reinterpret_cast<ipc::byte_t *>(this + 1);
123
+ }
124
+
125
+ chunk_t *at(std::size_t chunk_size, ipc::storage_id_t id) noexcept {
126
+ if (id < 0) return nullptr;
127
+ return reinterpret_cast<chunk_t *>(chunks_mem() + (chunk_size * id));
128
+ }
129
+ };
130
+
131
+ auto& chunk_storages() {
132
+ class chunk_handle_t {
133
+ ipc::shm::handle handle_;
134
+
135
+ public:
136
+ chunk_info_t *get_info(std::size_t chunk_size) {
137
+ if (!handle_.valid() &&
138
+ !handle_.acquire( ("__CHUNK_INFO__" + ipc::to_string(chunk_size)).c_str(),
139
+ sizeof(chunk_info_t) + chunk_info_t::chunks_mem_size(chunk_size) )) {
140
+ ipc::error("[chunk_storages] chunk_shm.id_info_.acquire failed: chunk_size = %zd\n", chunk_size);
141
+ return nullptr;
142
+ }
143
+ auto info = static_cast<chunk_info_t*>(handle_.get());
144
+ if (info == nullptr) {
145
+ ipc::error("[chunk_storages] chunk_shm.id_info_.get failed: chunk_size = %zd\n", chunk_size);
146
+ return nullptr;
147
+ }
148
+ return info;
149
+ }
150
+ };
151
+ static ipc::map<std::size_t, chunk_handle_t> chunk_hs;
152
+ return chunk_hs;
153
+ }
154
+
155
+ chunk_info_t *chunk_storage_info(std::size_t chunk_size) {
156
+ auto &storages = chunk_storages();
157
+ std::decay_t<decltype(storages)>::iterator it;
158
+ {
159
+ static ipc::rw_lock lock;
160
+ IPC_UNUSED_ std::shared_lock<ipc::rw_lock> guard {lock};
161
+ if ((it = storages.find(chunk_size)) == storages.end()) {
162
+ using chunk_handle_t = std::decay_t<decltype(storages)>::value_type::second_type;
163
+ guard.unlock();
164
+ IPC_UNUSED_ std::lock_guard<ipc::rw_lock> guard {lock};
165
+ it = storages.emplace(chunk_size, chunk_handle_t{}).first;
166
+ }
167
+ }
168
+ return it->second.get_info(chunk_size);
169
+ }
170
+
171
+ std::pair<ipc::storage_id_t, void*> acquire_storage(std::size_t size, ipc::circ::cc_t conns) {
172
+ std::size_t chunk_size = calc_chunk_size(size);
173
+ auto info = chunk_storage_info(chunk_size);
174
+ if (info == nullptr) return {};
175
+
176
+ info->lock_.lock();
177
+ info->pool_.prepare();
178
+ // got an unique id
179
+ auto id = info->pool_.acquire();
180
+ info->lock_.unlock();
181
+
182
+ auto chunk = info->at(chunk_size, id);
183
+ if (chunk == nullptr) return {};
184
+ chunk->conns().store(conns, std::memory_order_relaxed);
185
+ return { id, chunk->data() };
186
+ }
187
+
188
+ void *find_storage(ipc::storage_id_t id, std::size_t size) {
189
+ if (id < 0) {
190
+ ipc::error("[find_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
191
+ return nullptr;
192
+ }
193
+ std::size_t chunk_size = calc_chunk_size(size);
194
+ auto info = chunk_storage_info(chunk_size);
195
+ if (info == nullptr) return nullptr;
196
+ return info->at(chunk_size, id)->data();
197
+ }
198
+
199
+ void release_storage(ipc::storage_id_t id, std::size_t size) {
200
+ if (id < 0) {
201
+ ipc::error("[release_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
202
+ return;
203
+ }
204
+ std::size_t chunk_size = calc_chunk_size(size);
205
+ auto info = chunk_storage_info(chunk_size);
206
+ if (info == nullptr) return;
207
+ info->lock_.lock();
208
+ info->pool_.release(id);
209
+ info->lock_.unlock();
210
+ }
211
+
212
+ template <ipc::relat Rp, ipc::relat Rc>
213
+ bool sub_rc(ipc::wr<Rp, Rc, ipc::trans::unicast>,
214
+ std::atomic<ipc::circ::cc_t> &/*conns*/, ipc::circ::cc_t /*curr_conns*/, ipc::circ::cc_t /*conn_id*/) noexcept {
215
+ return true;
216
+ }
217
+
218
+ template <ipc::relat Rp, ipc::relat Rc>
219
+ bool sub_rc(ipc::wr<Rp, Rc, ipc::trans::broadcast>,
220
+ std::atomic<ipc::circ::cc_t> &conns, ipc::circ::cc_t curr_conns, ipc::circ::cc_t conn_id) noexcept {
221
+ auto last_conns = curr_conns & ~conn_id;
222
+ for (unsigned k = 0;;) {
223
+ auto chunk_conns = conns.load(std::memory_order_acquire);
224
+ if (conns.compare_exchange_weak(chunk_conns, chunk_conns & last_conns, std::memory_order_release)) {
225
+ return (chunk_conns & last_conns) == 0;
226
+ }
227
+ ipc::yield(k);
228
+ }
229
+ }
230
+
231
+ template <typename Flag>
232
+ void recycle_storage(ipc::storage_id_t id, std::size_t size, ipc::circ::cc_t curr_conns, ipc::circ::cc_t conn_id) {
233
+ if (id < 0) {
234
+ ipc::error("[recycle_storage] id is invalid: id = %ld, size = %zd\n", (long)id, size);
235
+ return;
236
+ }
237
+ std::size_t chunk_size = calc_chunk_size(size);
238
+ auto info = chunk_storage_info(chunk_size);
239
+ if (info == nullptr) return;
240
+
241
+ auto chunk = info->at(chunk_size, id);
242
+ if (chunk == nullptr) return;
243
+
244
+ if (!sub_rc(Flag{}, chunk->conns(), curr_conns, conn_id)) {
245
+ return;
246
+ }
247
+ info->lock_.lock();
248
+ info->pool_.release(id);
249
+ info->lock_.unlock();
250
+ }
251
+
252
+ template <typename MsgT>
253
+ bool clear_message(void* p) {
254
+ auto msg = static_cast<MsgT*>(p);
255
+ if (msg->storage_) {
256
+ std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg->remain_;
257
+ if (r_size <= 0) {
258
+ ipc::error("[clear_message] invalid msg size: %d\n", (int)r_size);
259
+ return true;
260
+ }
261
+ release_storage(
262
+ *reinterpret_cast<ipc::storage_id_t*>(&msg->data_),
263
+ static_cast<std::size_t>(r_size));
264
+ }
265
+ return true;
266
+ }
267
+
268
+ struct conn_info_head {
269
+
270
+ ipc::string name_;
271
+ msg_id_t cc_id_; // connection-info id
272
+ ipc::detail::waiter cc_waiter_, wt_waiter_, rd_waiter_;
273
+ ipc::shm::handle acc_h_;
274
+
275
+ conn_info_head(char const * name)
276
+ : name_ {name}
277
+ , cc_id_ {(cc_acc() == nullptr) ? 0 : cc_acc()->fetch_add(1, std::memory_order_relaxed)}
278
+ , cc_waiter_{("__CC_CONN__" + name_).c_str()}
279
+ , wt_waiter_{("__WT_CONN__" + name_).c_str()}
280
+ , rd_waiter_{("__RD_CONN__" + name_).c_str()}
281
+ , acc_h_ {("__AC_CONN__" + name_).c_str(), sizeof(acc_t)} {
282
+ }
283
+
284
+ void quit_waiting() {
285
+ cc_waiter_.quit_waiting();
286
+ wt_waiter_.quit_waiting();
287
+ rd_waiter_.quit_waiting();
288
+ }
289
+
290
+ auto acc() {
291
+ return static_cast<acc_t*>(acc_h_.get());
292
+ }
293
+
294
+ auto& recv_cache() {
295
+ thread_local ipc::unordered_map<msg_id_t, cache_t> tls;
296
+ return tls;
297
+ }
298
+ };
299
+
300
+ template <typename W, typename F>
301
+ bool wait_for(W& waiter, F&& pred, std::uint64_t tm) {
302
+ if (tm == 0) return !pred();
303
+ for (unsigned k = 0; pred();) {
304
+ bool ret = true;
305
+ ipc::sleep(k, [&k, &ret, &waiter, &pred, tm] {
306
+ ret = waiter.wait_if(std::forward<F>(pred), tm);
307
+ k = 0;
308
+ });
309
+ if (!ret) return false; // timeout or fail
310
+ if (k == 0) break; // k has been reset
311
+ }
312
+ return true;
313
+ }
314
+
315
+ template <typename Policy,
316
+ std::size_t DataSize = ipc::data_length,
317
+ std::size_t AlignSize = (ipc::detail::min)(DataSize, alignof(std::max_align_t))>
318
+ struct queue_generator {
319
+
320
+ using queue_t = ipc::queue<msg_t<DataSize, AlignSize>, Policy>;
321
+
322
+ struct conn_info_t : conn_info_head {
323
+ queue_t que_;
324
+
325
+ conn_info_t(char const * name)
326
+ : conn_info_head{name}
327
+ , que_{("__QU_CONN__" +
328
+ ipc::to_string(DataSize) + "__" +
329
+ ipc::to_string(AlignSize) + "__" + name).c_str()} {
330
+ }
331
+
332
+ void disconnect_receiver() {
333
+ bool dis = que_.disconnect();
334
+ this->quit_waiting();
335
+ if (dis) {
336
+ this->recv_cache().clear();
337
+ }
338
+ }
339
+ };
340
+ };
341
+
342
+ template <typename Policy>
343
+ struct detail_impl {
344
+
345
+ using policy_t = Policy;
346
+ using flag_t = typename policy_t::flag_t;
347
+ using queue_t = typename queue_generator<policy_t>::queue_t;
348
+ using conn_info_t = typename queue_generator<policy_t>::conn_info_t;
349
+
350
+ constexpr static conn_info_t* info_of(ipc::handle_t h) noexcept {
351
+ return static_cast<conn_info_t*>(h);
352
+ }
353
+
354
+ constexpr static queue_t* queue_of(ipc::handle_t h) noexcept {
355
+ return (info_of(h) == nullptr) ? nullptr : &(info_of(h)->que_);
356
+ }
357
+
358
+ /* API implementations */
359
+
360
+ static void disconnect(ipc::handle_t h) {
361
+ auto que = queue_of(h);
362
+ if (que == nullptr) {
363
+ return;
364
+ }
365
+ que->shut_sending();
366
+ assert(info_of(h) != nullptr);
367
+ info_of(h)->disconnect_receiver();
368
+ }
369
+
370
+ static bool reconnect(ipc::handle_t * ph, bool start_to_recv) {
371
+ assert(ph != nullptr);
372
+ assert(*ph != nullptr);
373
+ auto que = queue_of(*ph);
374
+ if (que == nullptr) {
375
+ return false;
376
+ }
377
+ if (start_to_recv) {
378
+ que->shut_sending();
379
+ if (que->connect()) { // wouldn't connect twice
380
+ info_of(*ph)->cc_waiter_.broadcast();
381
+ return true;
382
+ }
383
+ return false;
384
+ }
385
+ // start_to_recv == false
386
+ if (que->connected()) {
387
+ info_of(*ph)->disconnect_receiver();
388
+ }
389
+ return que->ready_sending();
390
+ }
391
+
392
+ static bool connect(ipc::handle_t * ph, char const * name, bool start_to_recv) {
393
+ assert(ph != nullptr);
394
+ if (*ph == nullptr) {
395
+ *ph = ipc::mem::alloc<conn_info_t>(name);
396
+ }
397
+ return reconnect(ph, start_to_recv);
398
+ }
399
+
400
+ static void destroy(ipc::handle_t h) {
401
+ disconnect(h);
402
+ ipc::mem::free(info_of(h));
403
+ }
404
+
405
+ static std::size_t recv_count(ipc::handle_t h) noexcept {
406
+ auto que = queue_of(h);
407
+ if (que == nullptr) {
408
+ return ipc::invalid_value;
409
+ }
410
+ return que->conn_count();
411
+ }
412
+
413
+ static bool wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) {
414
+ auto que = queue_of(h);
415
+ if (que == nullptr) {
416
+ return false;
417
+ }
418
+ return wait_for(info_of(h)->cc_waiter_, [que, r_count] {
419
+ return que->conn_count() < r_count;
420
+ }, tm);
421
+ }
422
+
423
+ template <typename F>
424
+ static bool send(F&& gen_push, ipc::handle_t h, void const * data, std::size_t size) {
425
+ if (data == nullptr || size == 0) {
426
+ ipc::error("fail: send(%p, %zd)\n", data, size);
427
+ return false;
428
+ }
429
+ auto que = queue_of(h);
430
+ if (que == nullptr) {
431
+ ipc::error("fail: send, queue_of(h) == nullptr\n");
432
+ return false;
433
+ }
434
+ if (que->elems() == nullptr) {
435
+ ipc::error("fail: send, queue_of(h)->elems() == nullptr\n");
436
+ return false;
437
+ }
438
+ if (!que->ready_sending()) {
439
+ ipc::error("fail: send, que->ready_sending() == false\n");
440
+ return false;
441
+ }
442
+ ipc::circ::cc_t conns = que->elems()->connections(std::memory_order_relaxed);
443
+ if (conns == 0) {
444
+ ipc::error("fail: send, there is no receiver on this connection.\n");
445
+ return false;
446
+ }
447
+ // calc a new message id
448
+ auto acc = info_of(h)->acc();
449
+ if (acc == nullptr) {
450
+ ipc::error("fail: send, info_of(h)->acc() == nullptr\n");
451
+ return false;
452
+ }
453
+ auto msg_id = acc->fetch_add(1, std::memory_order_relaxed);
454
+ auto try_push = std::forward<F>(gen_push)(info_of(h), que, msg_id);
455
+ if (size > ipc::large_msg_limit) {
456
+ auto dat = acquire_storage(size, conns);
457
+ void * buf = dat.second;
458
+ if (buf != nullptr) {
459
+ std::memcpy(buf, data, size);
460
+ return try_push(static_cast<std::int32_t>(size) -
461
+ static_cast<std::int32_t>(ipc::data_length), &(dat.first), 0);
462
+ }
463
+ // try using message fragment
464
+ //ipc::log("fail: shm::handle for big message. msg_id: %zd, size: %zd\n", msg_id, size);
465
+ }
466
+ // push message fragment
467
+ std::int32_t offset = 0;
468
+ for (std::int32_t i = 0; i < static_cast<std::int32_t>(size / ipc::data_length); ++i, offset += ipc::data_length) {
469
+ if (!try_push(static_cast<std::int32_t>(size) - offset - static_cast<std::int32_t>(ipc::data_length),
470
+ static_cast<ipc::byte_t const *>(data) + offset, ipc::data_length)) {
471
+ return false;
472
+ }
473
+ }
474
+ // if remain > 0, this is the last message fragment
475
+ std::int32_t remain = static_cast<std::int32_t>(size) - offset;
476
+ if (remain > 0) {
477
+ if (!try_push(remain - static_cast<std::int32_t>(ipc::data_length),
478
+ static_cast<ipc::byte_t const *>(data) + offset,
479
+ static_cast<std::size_t>(remain))) {
480
+ return false;
481
+ }
482
+ }
483
+ return true;
484
+ }
485
+
486
+ static bool send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
487
+ return send([tm](auto info, auto que, auto msg_id) {
488
+ return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) {
489
+ if (!wait_for(info->wt_waiter_, [&] {
490
+ return !que->push(
491
+ [](void*) { return true; },
492
+ info->cc_id_, msg_id, remain, data, size);
493
+ }, tm)) {
494
+ ipc::log("force_push: msg_id = %zd, remain = %d, size = %zd\n", msg_id, remain, size);
495
+ if (!que->force_push(
496
+ clear_message<typename queue_t::value_t>,
497
+ info->cc_id_, msg_id, remain, data, size)) {
498
+ return false;
499
+ }
500
+ }
501
+ info->rd_waiter_.broadcast();
502
+ return true;
503
+ };
504
+ }, h, data, size);
505
+ }
506
+
507
+ static bool try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
508
+ return send([tm](auto info, auto que, auto msg_id) {
509
+ return [tm, info, que, msg_id](std::int32_t remain, void const * data, std::size_t size) {
510
+ if (!wait_for(info->wt_waiter_, [&] {
511
+ return !que->push(
512
+ [](void*) { return true; },
513
+ info->cc_id_, msg_id, remain, data, size);
514
+ }, tm)) {
515
+ return false;
516
+ }
517
+ info->rd_waiter_.broadcast();
518
+ return true;
519
+ };
520
+ }, h, data, size);
521
+ }
522
+
523
+ static ipc::buff_t recv(ipc::handle_t h, std::uint64_t tm) {
524
+ auto que = queue_of(h);
525
+ if (que == nullptr) {
526
+ ipc::error("fail: recv, queue_of(h) == nullptr\n");
527
+ return {};
528
+ }
529
+ if (!que->connected()) {
530
+ // hasn't connected yet, just return.
531
+ return {};
532
+ }
533
+ auto& rc = info_of(h)->recv_cache();
534
+ for (;;) {
535
+ // pop a new message
536
+ typename queue_t::value_t msg;
537
+ if (!wait_for(info_of(h)->rd_waiter_, [que, &msg] {
538
+ return !que->pop(msg);
539
+ }, tm)) {
540
+ // pop failed, just return.
541
+ return {};
542
+ }
543
+ info_of(h)->wt_waiter_.broadcast();
544
+ if ((info_of(h)->acc() != nullptr) && (msg.cc_id_ == info_of(h)->cc_id_)) {
545
+ continue; // ignore message to self
546
+ }
547
+ // msg.remain_ may minus & abs(msg.remain_) < data_length
548
+ std::int32_t r_size = static_cast<std::int32_t>(ipc::data_length) + msg.remain_;
549
+ if (r_size <= 0) {
550
+ ipc::error("fail: recv, r_size = %d\n", (int)r_size);
551
+ return {};
552
+ }
553
+ std::size_t msg_size = static_cast<std::size_t>(r_size);
554
+ // large message
555
+ if (msg.storage_) {
556
+ ipc::storage_id_t buf_id = *reinterpret_cast<ipc::storage_id_t*>(&msg.data_);
557
+ void* buf = find_storage(buf_id, msg_size);
558
+ if (buf != nullptr) {
559
+ struct recycle_t {
560
+ ipc::storage_id_t storage_id;
561
+ ipc::circ::cc_t curr_conns;
562
+ ipc::circ::cc_t conn_id;
563
+ } *r_info = ipc::mem::alloc<recycle_t>(recycle_t{
564
+ buf_id, que->elems()->connections(std::memory_order_relaxed), que->connected_id()
565
+ });
566
+ if (r_info == nullptr) {
567
+ ipc::log("fail: ipc::mem::alloc<recycle_t>.\n");
568
+ return ipc::buff_t{buf, msg_size}; // no recycle
569
+ } else {
570
+ return ipc::buff_t{buf, msg_size, [](void* p_info, std::size_t size) {
571
+ auto r_info = static_cast<recycle_t *>(p_info);
572
+ IPC_UNUSED_ auto finally = ipc::guard([r_info] {
573
+ ipc::mem::free(r_info);
574
+ });
575
+ recycle_storage<flag_t>(r_info->storage_id, size, r_info->curr_conns, r_info->conn_id);
576
+ }, r_info};
577
+ }
578
+ } else {
579
+ ipc::log("fail: shm::handle for large message. msg_id: %zd, buf_id: %zd, size: %zd\n", msg.id_, buf_id, msg_size);
580
+ continue;
581
+ }
582
+ }
583
+ // find cache with msg.id_
584
+ auto cac_it = rc.find(msg.id_);
585
+ if (cac_it == rc.end()) {
586
+ if (msg_size <= ipc::data_length) {
587
+ return make_cache(msg.data_, msg_size);
588
+ }
589
+ // gc
590
+ if (rc.size() > 1024) {
591
+ std::vector<msg_id_t> need_del;
592
+ for (auto const & pair : rc) {
593
+ auto cmp = std::minmax(msg.id_, pair.first);
594
+ if (cmp.second - cmp.first > 8192) {
595
+ need_del.push_back(pair.first);
596
+ }
597
+ }
598
+ for (auto id : need_del) rc.erase(id);
599
+ }
600
+ // cache the first message fragment
601
+ rc.emplace(msg.id_, cache_t { ipc::data_length, make_cache(msg.data_, msg_size) });
602
+ }
603
+ // has cached before this message
604
+ else {
605
+ auto& cac = cac_it->second;
606
+ // this is the last message fragment
607
+ if (msg.remain_ <= 0) {
608
+ cac.append(&(msg.data_), msg_size);
609
+ // finish this message, erase it from cache
610
+ auto buff = std::move(cac.buff_);
611
+ rc.erase(cac_it);
612
+ return buff;
613
+ }
614
+ // there are remain datas after this message
615
+ cac.append(&(msg.data_), ipc::data_length);
616
+ }
617
+ }
618
+ }
619
+
620
+ static ipc::buff_t try_recv(ipc::handle_t h) {
621
+ return recv(h, 0);
622
+ }
623
+
624
+ }; // detail_impl<Policy>
625
+
626
+ template <typename Flag>
627
+ using policy_t = ipc::policy::choose<ipc::circ::elem_array, Flag>;
628
+
629
+ } // internal-linkage
630
+
631
+ namespace ipc {
632
+
633
+ template <typename Flag>
634
+ ipc::handle_t chan_impl<Flag>::inited() {
635
+ ipc::detail::waiter::init();
636
+ return nullptr;
637
+ }
638
+
639
+ template <typename Flag>
640
+ bool chan_impl<Flag>::connect(ipc::handle_t * ph, char const * name, unsigned mode) {
641
+ return detail_impl<policy_t<Flag>>::connect(ph, name, mode & receiver);
642
+ }
643
+
644
+ template <typename Flag>
645
+ bool chan_impl<Flag>::reconnect(ipc::handle_t * ph, unsigned mode) {
646
+ return detail_impl<policy_t<Flag>>::reconnect(ph, mode & receiver);
647
+ }
648
+
649
+ template <typename Flag>
650
+ void chan_impl<Flag>::disconnect(ipc::handle_t h) {
651
+ detail_impl<policy_t<Flag>>::disconnect(h);
652
+ }
653
+
654
+ template <typename Flag>
655
+ void chan_impl<Flag>::destroy(ipc::handle_t h) {
656
+ detail_impl<policy_t<Flag>>::destroy(h);
657
+ }
658
+
659
+ template <typename Flag>
660
+ char const * chan_impl<Flag>::name(ipc::handle_t h) {
661
+ auto info = detail_impl<policy_t<Flag>>::info_of(h);
662
+ return (info == nullptr) ? nullptr : info->name_.c_str();
663
+ }
664
+
665
+ template <typename Flag>
666
+ std::size_t chan_impl<Flag>::recv_count(ipc::handle_t h) {
667
+ return detail_impl<policy_t<Flag>>::recv_count(h);
668
+ }
669
+
670
+ template <typename Flag>
671
+ bool chan_impl<Flag>::wait_for_recv(ipc::handle_t h, std::size_t r_count, std::uint64_t tm) {
672
+ return detail_impl<policy_t<Flag>>::wait_for_recv(h, r_count, tm);
673
+ }
674
+
675
+ template <typename Flag>
676
+ bool chan_impl<Flag>::send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
677
+ return detail_impl<policy_t<Flag>>::send(h, data, size, tm);
678
+ }
679
+
680
+ template <typename Flag>
681
+ buff_t chan_impl<Flag>::recv(ipc::handle_t h, std::uint64_t tm) {
682
+ return detail_impl<policy_t<Flag>>::recv(h, tm);
683
+ }
684
+
685
+ template <typename Flag>
686
+ bool chan_impl<Flag>::try_send(ipc::handle_t h, void const * data, std::size_t size, std::uint64_t tm) {
687
+ return detail_impl<policy_t<Flag>>::try_send(h, data, size, tm);
688
+ }
689
+
690
+ template <typename Flag>
691
+ buff_t chan_impl<Flag>::try_recv(ipc::handle_t h) {
692
+ return detail_impl<policy_t<Flag>>::try_recv(h);
693
+ }
694
+
695
+ template struct chan_impl<ipc::wr<relat::single, relat::single, trans::unicast >>;
696
+ // template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::unicast >>; // TBD
697
+ // template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::unicast >>; // TBD
698
+ template struct chan_impl<ipc::wr<relat::single, relat::multi , trans::broadcast>>;
699
+ template struct chan_impl<ipc::wr<relat::multi , relat::multi , trans::broadcast>>;
700
+
701
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/policy.h ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <type_traits>
4
+
5
+ #include "libipc/def.h"
6
+ #include "libipc/prod_cons.h"
7
+
8
+ #include "libipc/circ/elem_array.h"
9
+
10
+ namespace ipc {
11
+ namespace policy {
12
+
13
+ template <template <typename, std::size_t...> class Elems, typename Flag>
14
+ struct choose;
15
+
16
+ template <typename Flag>
17
+ struct choose<circ::elem_array, Flag> {
18
+ using flag_t = Flag;
19
+
20
+ template <std::size_t DataSize, std::size_t AlignSize>
21
+ using elems_t = circ::elem_array<ipc::prod_cons_impl<flag_t>, DataSize, AlignSize>;
22
+ };
23
+
24
+ } // namespace policy
25
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/pool_alloc.cpp ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include "libipc/pool_alloc.h"
2
+
3
+ #include "libipc/memory/resource.h"
4
+
5
+ namespace ipc {
6
+ namespace mem {
7
+
8
+ void* pool_alloc::alloc(std::size_t size) {
9
+ return async_pool_alloc::alloc(size);
10
+ }
11
+
12
+ void pool_alloc::free(void* p, std::size_t size) {
13
+ async_pool_alloc::free(p, size);
14
+ }
15
+
16
+ } // namespace mem
17
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/prod_cons.h ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <atomic>
4
+ #include <utility>
5
+ #include <cstring>
6
+ #include <type_traits>
7
+ #include <cstdint>
8
+
9
+ #include "libipc/def.h"
10
+
11
+ #include "libipc/platform/detail.h"
12
+ #include "libipc/circ/elem_def.h"
13
+ #include "libipc/utility/log.h"
14
+ #include "libipc/utility/utility.h"
15
+
16
+ namespace ipc {
17
+
18
+ ////////////////////////////////////////////////////////////////
19
+ /// producer-consumer implementation
20
+ ////////////////////////////////////////////////////////////////
21
+
22
+ template <typename Flag>
23
+ struct prod_cons_impl;
24
+
25
+ template <>
26
+ struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
27
+
28
+ template <std::size_t DataSize, std::size_t AlignSize>
29
+ struct elem_t {
30
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
31
+ };
32
+
33
+ alignas(cache_line_size) std::atomic<circ::u2_t> rd_; // read index
34
+ alignas(cache_line_size) std::atomic<circ::u2_t> wt_; // write index
35
+
36
+ constexpr circ::u2_t cursor() const noexcept {
37
+ return 0;
38
+ }
39
+
40
+ template <typename W, typename F, typename E>
41
+ bool push(W* /*wrapper*/, F&& f, E* elems) {
42
+ auto cur_wt = circ::index_of(wt_.load(std::memory_order_relaxed));
43
+ if (cur_wt == circ::index_of(rd_.load(std::memory_order_acquire) - 1)) {
44
+ return false; // full
45
+ }
46
+ std::forward<F>(f)(&(elems[cur_wt].data_));
47
+ wt_.fetch_add(1, std::memory_order_release);
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * In single-single-unicast, 'force_push' means 'no reader' or 'the only one reader is dead'.
53
+ * So we could just disconnect all connections of receiver, and return false.
54
+ */
55
+ template <typename W, typename F, typename E>
56
+ bool force_push(W* wrapper, F&&, E*) {
57
+ wrapper->elems()->disconnect_receiver(~static_cast<circ::cc_t>(0u));
58
+ return false;
59
+ }
60
+
61
+ template <typename W, typename F, typename R, typename E>
62
+ bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, R&& out, E* elems) {
63
+ auto cur_rd = circ::index_of(rd_.load(std::memory_order_relaxed));
64
+ if (cur_rd == circ::index_of(wt_.load(std::memory_order_acquire))) {
65
+ return false; // empty
66
+ }
67
+ std::forward<F>(f)(&(elems[cur_rd].data_));
68
+ std::forward<R>(out)(true);
69
+ rd_.fetch_add(1, std::memory_order_release);
70
+ return true;
71
+ }
72
+ };
73
+
74
+ template <>
75
+ struct prod_cons_impl<wr<relat::single, relat::multi , trans::unicast>>
76
+ : prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
77
+
78
+ template <typename W, typename F, typename E>
79
+ bool force_push(W* wrapper, F&&, E*) {
80
+ wrapper->elems()->disconnect_receiver(1);
81
+ return false;
82
+ }
83
+
84
+ template <typename W, typename F, typename R,
85
+ template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
86
+ bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, R&& out, E<DS, AS>* elems) {
87
+ byte_t buff[DS];
88
+ for (unsigned k = 0;;) {
89
+ auto cur_rd = rd_.load(std::memory_order_relaxed);
90
+ if (circ::index_of(cur_rd) ==
91
+ circ::index_of(wt_.load(std::memory_order_acquire))) {
92
+ return false; // empty
93
+ }
94
+ std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
95
+ if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
96
+ std::forward<F>(f)(buff);
97
+ std::forward<R>(out)(true);
98
+ return true;
99
+ }
100
+ ipc::yield(k);
101
+ }
102
+ }
103
+ };
104
+
105
+ template <>
106
+ struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>>
107
+ : prod_cons_impl<wr<relat::single, relat::multi, trans::unicast>> {
108
+
109
+ using flag_t = std::uint64_t;
110
+
111
+ template <std::size_t DataSize, std::size_t AlignSize>
112
+ struct elem_t {
113
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
114
+ std::atomic<flag_t> f_ct_ { 0 }; // commit flag
115
+ };
116
+
117
+ alignas(cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
118
+
119
+ template <typename W, typename F, typename E>
120
+ bool push(W* /*wrapper*/, F&& f, E* elems) {
121
+ circ::u2_t cur_ct, nxt_ct;
122
+ for (unsigned k = 0;;) {
123
+ cur_ct = ct_.load(std::memory_order_relaxed);
124
+ if (circ::index_of(nxt_ct = cur_ct + 1) ==
125
+ circ::index_of(rd_.load(std::memory_order_acquire))) {
126
+ return false; // full
127
+ }
128
+ if (ct_.compare_exchange_weak(cur_ct, nxt_ct, std::memory_order_acq_rel)) {
129
+ break;
130
+ }
131
+ ipc::yield(k);
132
+ }
133
+ auto* el = elems + circ::index_of(cur_ct);
134
+ std::forward<F>(f)(&(el->data_));
135
+ // set flag & try update wt
136
+ el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
137
+ while (1) {
138
+ auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
139
+ if (cur_ct != wt_.load(std::memory_order_relaxed)) {
140
+ return true;
141
+ }
142
+ if ((~cac_ct) != cur_ct) {
143
+ return true;
144
+ }
145
+ if (!el->f_ct_.compare_exchange_strong(cac_ct, 0, std::memory_order_relaxed)) {
146
+ return true;
147
+ }
148
+ wt_.store(nxt_ct, std::memory_order_release);
149
+ cur_ct = nxt_ct;
150
+ nxt_ct = cur_ct + 1;
151
+ el = elems + circ::index_of(cur_ct);
152
+ }
153
+ return true;
154
+ }
155
+
156
+ template <typename W, typename F, typename E>
157
+ bool force_push(W* wrapper, F&&, E*) {
158
+ wrapper->elems()->disconnect_receiver(1);
159
+ return false;
160
+ }
161
+
162
+ template <typename W, typename F, typename R,
163
+ template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
164
+ bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, R&& out, E<DS, AS>* elems) {
165
+ byte_t buff[DS];
166
+ for (unsigned k = 0;;) {
167
+ auto cur_rd = rd_.load(std::memory_order_relaxed);
168
+ auto cur_wt = wt_.load(std::memory_order_acquire);
169
+ auto id_rd = circ::index_of(cur_rd);
170
+ auto id_wt = circ::index_of(cur_wt);
171
+ if (id_rd == id_wt) {
172
+ auto* el = elems + id_wt;
173
+ auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
174
+ if ((~cac_ct) != cur_wt) {
175
+ return false; // empty
176
+ }
177
+ if (el->f_ct_.compare_exchange_weak(cac_ct, 0, std::memory_order_relaxed)) {
178
+ wt_.store(cur_wt + 1, std::memory_order_release);
179
+ }
180
+ k = 0;
181
+ }
182
+ else {
183
+ std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
184
+ if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
185
+ std::forward<F>(f)(buff);
186
+ std::forward<R>(out)(true);
187
+ return true;
188
+ }
189
+ ipc::yield(k);
190
+ }
191
+ }
192
+ }
193
+ };
194
+
195
+ template <>
196
+ struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
197
+
198
+ using rc_t = std::uint64_t;
199
+
200
+ enum : rc_t {
201
+ ep_mask = 0x00000000ffffffffull,
202
+ ep_incr = 0x0000000100000000ull
203
+ };
204
+
205
+ template <std::size_t DataSize, std::size_t AlignSize>
206
+ struct elem_t {
207
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
208
+ std::atomic<rc_t> rc_ { 0 }; // read-counter
209
+ };
210
+
211
+ alignas(cache_line_size) std::atomic<circ::u2_t> wt_; // write index
212
+ alignas(cache_line_size) rc_t epoch_ { 0 }; // only one writer
213
+
214
+ circ::u2_t cursor() const noexcept {
215
+ return wt_.load(std::memory_order_acquire);
216
+ }
217
+
218
+ template <typename W, typename F, typename E>
219
+ bool push(W* wrapper, F&& f, E* elems) {
220
+ E* el;
221
+ for (unsigned k = 0;;) {
222
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
223
+ if (cc == 0) return false; // no reader
224
+ el = elems + circ::index_of(wt_.load(std::memory_order_relaxed));
225
+ // check all consumers have finished reading this element
226
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
227
+ circ::cc_t rem_cc = cur_rc & ep_mask;
228
+ if ((cc & rem_cc) && ((cur_rc & ~ep_mask) == epoch_)) {
229
+ return false; // has not finished yet
230
+ }
231
+ // consider rem_cc to be 0 here
232
+ if (el->rc_.compare_exchange_weak(
233
+ cur_rc, epoch_ | static_cast<rc_t>(cc), std::memory_order_release)) {
234
+ break;
235
+ }
236
+ ipc::yield(k);
237
+ }
238
+ std::forward<F>(f)(&(el->data_));
239
+ wt_.fetch_add(1, std::memory_order_release);
240
+ return true;
241
+ }
242
+
243
+ template <typename W, typename F, typename E>
244
+ bool force_push(W* wrapper, F&& f, E* elems) {
245
+ E* el;
246
+ epoch_ += ep_incr;
247
+ for (unsigned k = 0;;) {
248
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
249
+ if (cc == 0) return false; // no reader
250
+ el = elems + circ::index_of(wt_.load(std::memory_order_relaxed));
251
+ // check all consumers have finished reading this element
252
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
253
+ circ::cc_t rem_cc = cur_rc & ep_mask;
254
+ if (cc & rem_cc) {
255
+ ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
256
+ cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
257
+ if (cc == 0) return false; // no reader
258
+ }
259
+ // just compare & exchange
260
+ if (el->rc_.compare_exchange_weak(
261
+ cur_rc, epoch_ | static_cast<rc_t>(cc), std::memory_order_release)) {
262
+ break;
263
+ }
264
+ ipc::yield(k);
265
+ }
266
+ std::forward<F>(f)(&(el->data_));
267
+ wt_.fetch_add(1, std::memory_order_release);
268
+ return true;
269
+ }
270
+
271
+ template <typename W, typename F, typename R, typename E>
272
+ bool pop(W* wrapper, circ::u2_t& cur, F&& f, R&& out, E* elems) {
273
+ if (cur == cursor()) return false; // acquire
274
+ auto* el = elems + circ::index_of(cur++);
275
+ std::forward<F>(f)(&(el->data_));
276
+ for (unsigned k = 0;;) {
277
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
278
+ if ((cur_rc & ep_mask) == 0) {
279
+ std::forward<R>(out)(true);
280
+ return true;
281
+ }
282
+ auto nxt_rc = cur_rc & ~static_cast<rc_t>(wrapper->connected_id());
283
+ if (el->rc_.compare_exchange_weak(cur_rc, nxt_rc, std::memory_order_release)) {
284
+ std::forward<R>(out)((nxt_rc & ep_mask) == 0);
285
+ return true;
286
+ }
287
+ ipc::yield(k);
288
+ }
289
+ }
290
+ };
291
+
292
+ template <>
293
+ struct prod_cons_impl<wr<relat::multi, relat::multi, trans::broadcast>> {
294
+
295
+ using rc_t = std::uint64_t;
296
+ using flag_t = std::uint64_t;
297
+
298
+ enum : rc_t {
299
+ rc_mask = 0x00000000ffffffffull,
300
+ ep_mask = 0x00ffffffffffffffull,
301
+ ep_incr = 0x0100000000000000ull,
302
+ ic_mask = 0xff000000ffffffffull,
303
+ ic_incr = 0x0000000100000000ull
304
+ };
305
+
306
+ template <std::size_t DataSize, std::size_t AlignSize>
307
+ struct elem_t {
308
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
309
+ std::atomic<rc_t > rc_ { 0 }; // read-counter
310
+ std::atomic<flag_t> f_ct_ { 0 }; // commit flag
311
+ };
312
+
313
+ alignas(cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
314
+ alignas(cache_line_size) std::atomic<rc_t> epoch_ { 0 };
315
+
316
+ circ::u2_t cursor() const noexcept {
317
+ return ct_.load(std::memory_order_acquire);
318
+ }
319
+
320
+ constexpr static rc_t inc_rc(rc_t rc) noexcept {
321
+ return (rc & ic_mask) | ((rc + ic_incr) & ~ic_mask);
322
+ }
323
+
324
+ constexpr static rc_t inc_mask(rc_t rc) noexcept {
325
+ return inc_rc(rc) & ~rc_mask;
326
+ }
327
+
328
+ template <typename W, typename F, typename E>
329
+ bool push(W* wrapper, F&& f, E* elems) {
330
+ E* el;
331
+ circ::u2_t cur_ct;
332
+ rc_t epoch = epoch_.load(std::memory_order_acquire);
333
+ for (unsigned k = 0;;) {
334
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
335
+ if (cc == 0) return false; // no reader
336
+ el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
337
+ // check all consumers have finished reading this element
338
+ auto cur_rc = el->rc_.load(std::memory_order_relaxed);
339
+ circ::cc_t rem_cc = cur_rc & rc_mask;
340
+ if ((cc & rem_cc) && ((cur_rc & ~ep_mask) == epoch)) {
341
+ return false; // has not finished yet
342
+ }
343
+ else if (!rem_cc) {
344
+ auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
345
+ if ((cur_fl != cur_ct) && cur_fl) {
346
+ return false; // full
347
+ }
348
+ }
349
+ // consider rem_cc to be 0 here
350
+ if (el->rc_.compare_exchange_weak(
351
+ cur_rc, inc_mask(epoch | (cur_rc & ep_mask)) | static_cast<rc_t>(cc), std::memory_order_relaxed) &&
352
+ epoch_.compare_exchange_weak(epoch, epoch, std::memory_order_acq_rel)) {
353
+ break;
354
+ }
355
+ ipc::yield(k);
356
+ }
357
+ // only one thread/process would touch here at one time
358
+ ct_.store(cur_ct + 1, std::memory_order_release);
359
+ std::forward<F>(f)(&(el->data_));
360
+ // set flag & try update wt
361
+ el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
362
+ return true;
363
+ }
364
+
365
+ template <typename W, typename F, typename E>
366
+ bool force_push(W* wrapper, F&& f, E* elems) {
367
+ E* el;
368
+ circ::u2_t cur_ct;
369
+ rc_t epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
370
+ for (unsigned k = 0;;) {
371
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
372
+ if (cc == 0) return false; // no reader
373
+ el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
374
+ // check all consumers have finished reading this element
375
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
376
+ circ::cc_t rem_cc = cur_rc & rc_mask;
377
+ if (cc & rem_cc) {
378
+ ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
379
+ cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
380
+ if (cc == 0) return false; // no reader
381
+ }
382
+ // just compare & exchange
383
+ if (el->rc_.compare_exchange_weak(
384
+ cur_rc, inc_mask(epoch | (cur_rc & ep_mask)) | static_cast<rc_t>(cc), std::memory_order_relaxed)) {
385
+ if (epoch == epoch_.load(std::memory_order_acquire)) {
386
+ break;
387
+ }
388
+ else if (push(wrapper, std::forward<F>(f), elems)) {
389
+ return true;
390
+ }
391
+ epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
392
+ }
393
+ ipc::yield(k);
394
+ }
395
+ // only one thread/process would touch here at one time
396
+ ct_.store(cur_ct + 1, std::memory_order_release);
397
+ std::forward<F>(f)(&(el->data_));
398
+ // set flag & try update wt
399
+ el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
400
+ return true;
401
+ }
402
+
403
+ template <typename W, typename F, typename R, typename E, std::size_t N>
404
+ bool pop(W* wrapper, circ::u2_t& cur, F&& f, R&& out, E(& elems)[N]) {
405
+ auto* el = elems + circ::index_of(cur);
406
+ auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
407
+ if (cur_fl != ~static_cast<flag_t>(cur)) {
408
+ return false; // empty
409
+ }
410
+ ++cur;
411
+ std::forward<F>(f)(&(el->data_));
412
+ for (unsigned k = 0;;) {
413
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
414
+ if ((cur_rc & rc_mask) == 0) {
415
+ std::forward<R>(out)(true);
416
+ el->f_ct_.store(cur + N - 1, std::memory_order_release);
417
+ return true;
418
+ }
419
+ auto nxt_rc = inc_rc(cur_rc) & ~static_cast<rc_t>(wrapper->connected_id());
420
+ bool last_one = false;
421
+ if ((last_one = (nxt_rc & rc_mask) == 0)) {
422
+ el->f_ct_.store(cur + N - 1, std::memory_order_release);
423
+ }
424
+ if (el->rc_.compare_exchange_weak(cur_rc, nxt_rc, std::memory_order_release)) {
425
+ std::forward<R>(out)(last_one);
426
+ return true;
427
+ }
428
+ ipc::yield(k);
429
+ }
430
+ }
431
+ };
432
+
433
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/queue.h ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <type_traits>
4
+ #include <new>
5
+ #include <utility> // [[since C++14]]: std::exchange
6
+ #include <algorithm>
7
+ #include <atomic>
8
+ #include <tuple>
9
+ #include <thread>
10
+ #include <chrono>
11
+ #include <string>
12
+ #include <cassert> // assert
13
+
14
+ #include "libipc/def.h"
15
+ #include "libipc/shm.h"
16
+ #include "libipc/rw_lock.h"
17
+
18
+ #include "libipc/utility/log.h"
19
+ #include "libipc/platform/detail.h"
20
+ #include "libipc/circ/elem_def.h"
21
+
22
+ namespace ipc {
23
+ namespace detail {
24
+
25
+ class queue_conn {
26
+ protected:
27
+ circ::cc_t connected_ = 0;
28
+ shm::handle elems_h_;
29
+
30
+ template <typename Elems>
31
+ Elems* open(char const * name) {
32
+ if (name == nullptr || name[0] == '\0') {
33
+ ipc::error("fail open waiter: name is empty!\n");
34
+ return nullptr;
35
+ }
36
+ if (!elems_h_.acquire(name, sizeof(Elems))) {
37
+ return nullptr;
38
+ }
39
+ auto elems = static_cast<Elems*>(elems_h_.get());
40
+ if (elems == nullptr) {
41
+ ipc::error("fail acquire elems: %s\n", name);
42
+ return nullptr;
43
+ }
44
+ elems->init();
45
+ return elems;
46
+ }
47
+
48
+ void close() {
49
+ elems_h_.release();
50
+ }
51
+
52
+ public:
53
+ queue_conn() = default;
54
+ queue_conn(const queue_conn&) = delete;
55
+ queue_conn& operator=(const queue_conn&) = delete;
56
+
57
+ bool connected() const noexcept {
58
+ return connected_ != 0;
59
+ }
60
+
61
+ circ::cc_t connected_id() const noexcept {
62
+ return connected_;
63
+ }
64
+
65
+ template <typename Elems>
66
+ auto connect(Elems* elems) noexcept
67
+ /*needs 'optional' here*/
68
+ -> std::tuple<bool, bool, decltype(std::declval<Elems>().cursor())> {
69
+ if (elems == nullptr) return {};
70
+ // if it's already connected, just return
71
+ if (connected()) return {connected(), false, 0};
72
+ connected_ = elems->connect_receiver();
73
+ return {connected(), true, elems->cursor()};
74
+ }
75
+
76
+ template <typename Elems>
77
+ bool disconnect(Elems* elems) noexcept {
78
+ if (elems == nullptr) return false;
79
+ // if it's already disconnected, just return false
80
+ if (!connected()) return false;
81
+ elems->disconnect_receiver(std::exchange(connected_, 0));
82
+ return true;
83
+ }
84
+ };
85
+
86
+ template <typename Elems>
87
+ class queue_base : public queue_conn {
88
+ using base_t = queue_conn;
89
+
90
+ public:
91
+ using elems_t = Elems;
92
+ using policy_t = typename elems_t::policy_t;
93
+
94
+ protected:
95
+ elems_t * elems_ = nullptr;
96
+ decltype(std::declval<elems_t>().cursor()) cursor_ = 0;
97
+ bool sender_flag_ = false;
98
+
99
+ public:
100
+ using base_t::base_t;
101
+
102
+ queue_base() = default;
103
+
104
+ explicit queue_base(char const * name)
105
+ : queue_base{} {
106
+ elems_ = open<elems_t>(name);
107
+ }
108
+
109
+ explicit queue_base(elems_t * elems) noexcept
110
+ : queue_base{} {
111
+ assert(elems != nullptr);
112
+ elems_ = elems;
113
+ }
114
+
115
+ /* not virtual */ ~queue_base() {
116
+ base_t::close();
117
+ }
118
+
119
+ elems_t * elems() noexcept { return elems_; }
120
+ elems_t const * elems() const noexcept { return elems_; }
121
+
122
+ bool ready_sending() noexcept {
123
+ if (elems_ == nullptr) return false;
124
+ return sender_flag_ || (sender_flag_ = elems_->connect_sender());
125
+ }
126
+
127
+ void shut_sending() noexcept {
128
+ if (elems_ == nullptr) return;
129
+ if (!sender_flag_) return;
130
+ elems_->disconnect_sender();
131
+ }
132
+
133
+ bool connect() noexcept {
134
+ auto tp = base_t::connect(elems_);
135
+ if (std::get<0>(tp) && std::get<1>(tp)) {
136
+ cursor_ = std::get<2>(tp);
137
+ return true;
138
+ }
139
+ return std::get<0>(tp);
140
+ }
141
+
142
+ bool disconnect() noexcept {
143
+ return base_t::disconnect(elems_);
144
+ }
145
+
146
+ std::size_t conn_count() const noexcept {
147
+ return (elems_ == nullptr) ? static_cast<std::size_t>(invalid_value) : elems_->conn_count();
148
+ }
149
+
150
+ bool valid() const noexcept {
151
+ return elems_ != nullptr;
152
+ }
153
+
154
+ bool empty() const noexcept {
155
+ return !valid() || (cursor_ == elems_->cursor());
156
+ }
157
+
158
+ template <typename T, typename F, typename... P>
159
+ bool push(F&& prep, P&&... params) {
160
+ if (elems_ == nullptr) return false;
161
+ return elems_->push(this, [&](void* p) {
162
+ if (prep(p)) ::new (p) T(std::forward<P>(params)...);
163
+ });
164
+ }
165
+
166
+ template <typename T, typename F, typename... P>
167
+ bool force_push(F&& prep, P&&... params) {
168
+ if (elems_ == nullptr) return false;
169
+ return elems_->force_push(this, [&](void* p) {
170
+ if (prep(p)) ::new (p) T(std::forward<P>(params)...);
171
+ });
172
+ }
173
+
174
+ template <typename T, typename F>
175
+ bool pop(T& item, F&& out) {
176
+ if (elems_ == nullptr) {
177
+ return false;
178
+ }
179
+ return elems_->pop(this, &(this->cursor_), [&item](void* p) {
180
+ ::new (&item) T(std::move(*static_cast<T*>(p)));
181
+ }, std::forward<F>(out));
182
+ }
183
+ };
184
+
185
+ } // namespace detail
186
+
187
+ template <typename T, typename Policy>
188
+ class queue final : public detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>> {
189
+ using base_t = detail::queue_base<typename Policy::template elems_t<sizeof(T), alignof(T)>>;
190
+
191
+ public:
192
+ using value_t = T;
193
+
194
+ using base_t::base_t;
195
+
196
+ template <typename... P>
197
+ bool push(P&&... params) {
198
+ return base_t::template push<T>(std::forward<P>(params)...);
199
+ }
200
+
201
+ template <typename... P>
202
+ bool force_push(P&&... params) {
203
+ return base_t::template force_push<T>(std::forward<P>(params)...);
204
+ }
205
+
206
+ bool pop(T& item) {
207
+ return base_t::pop(item, [](bool) {});
208
+ }
209
+
210
+ template <typename F>
211
+ bool pop(T& item, F&& out) {
212
+ return base_t::pop(item, std::forward<F>(out));
213
+ }
214
+ };
215
+
216
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/shm.cpp ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ #include <string>
3
+ #include <utility>
4
+
5
+ #include "libipc/shm.h"
6
+
7
+ #include "libipc/utility/pimpl.h"
8
+ #include "libipc/memory/resource.h"
9
+
10
+ namespace ipc {
11
+ namespace shm {
12
+
13
+ class handle::handle_ : public pimpl<handle_> {
14
+ public:
15
+ shm::id_t id_ = nullptr;
16
+ void* m_ = nullptr;
17
+
18
+ ipc::string n_;
19
+ std::size_t s_ = 0;
20
+ };
21
+
22
+ handle::handle()
23
+ : p_(p_->make()) {
24
+ }
25
+
26
+ handle::handle(char const * name, std::size_t size, unsigned mode)
27
+ : handle() {
28
+ acquire(name, size, mode);
29
+ }
30
+
31
+ handle::handle(handle&& rhs)
32
+ : handle() {
33
+ swap(rhs);
34
+ }
35
+
36
+ handle::~handle() {
37
+ release();
38
+ p_->clear();
39
+ }
40
+
41
+ void handle::swap(handle& rhs) {
42
+ std::swap(p_, rhs.p_);
43
+ }
44
+
45
+ handle& handle::operator=(handle rhs) {
46
+ swap(rhs);
47
+ return *this;
48
+ }
49
+
50
+ bool handle::valid() const noexcept {
51
+ return impl(p_)->m_ != nullptr;
52
+ }
53
+
54
+ std::size_t handle::size() const noexcept {
55
+ return impl(p_)->s_;
56
+ }
57
+
58
+ char const * handle::name() const noexcept {
59
+ return impl(p_)->n_.c_str();
60
+ }
61
+
62
+ std::int32_t handle::ref() const noexcept {
63
+ return shm::get_ref(impl(p_)->id_);
64
+ }
65
+
66
+ void handle::sub_ref() noexcept {
67
+ shm::sub_ref(impl(p_)->id_);
68
+ }
69
+
70
+ bool handle::acquire(char const * name, std::size_t size, unsigned mode) {
71
+ release();
72
+ impl(p_)->id_ = shm::acquire((impl(p_)->n_ = name).c_str(), size, mode);
73
+ impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
74
+ return valid();
75
+ }
76
+
77
+ std::int32_t handle::release() {
78
+ if (impl(p_)->id_ == nullptr) return -1;
79
+ return shm::release(detach());
80
+ }
81
+
82
+ void* handle::get() const {
83
+ return impl(p_)->m_;
84
+ }
85
+
86
+ void handle::attach(id_t id) {
87
+ if (id == nullptr) return;
88
+ release();
89
+ impl(p_)->id_ = id;
90
+ impl(p_)->m_ = shm::get_mem(impl(p_)->id_, &(impl(p_)->s_));
91
+ }
92
+
93
+ id_t handle::detach() {
94
+ auto old = impl(p_)->id_;
95
+ impl(p_)->id_ = nullptr;
96
+ impl(p_)->m_ = nullptr;
97
+ impl(p_)->s_ = 0;
98
+ impl(p_)->n_.clear();
99
+ return old;
100
+ }
101
+
102
+ } // namespace shm
103
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/waiter.h ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <utility>
4
+ #include <string>
5
+ #include <mutex>
6
+ #include <atomic>
7
+
8
+ #include "libipc/def.h"
9
+ #include "libipc/mutex.h"
10
+ #include "libipc/condition.h"
11
+ #include "libipc/platform/detail.h"
12
+
13
+ namespace ipc {
14
+ namespace detail {
15
+
16
+ class waiter {
17
+ ipc::sync::condition cond_;
18
+ ipc::sync::mutex lock_;
19
+ std::atomic<bool> quit_ {false};
20
+
21
+ public:
22
+ static void init();
23
+
24
+ waiter() = default;
25
+ waiter(char const *name) {
26
+ open(name);
27
+ }
28
+
29
+ ~waiter() {
30
+ close();
31
+ }
32
+
33
+ bool valid() const noexcept {
34
+ return cond_.valid() && lock_.valid();
35
+ }
36
+
37
+ bool open(char const *name) noexcept {
38
+ quit_.store(false, std::memory_order_relaxed);
39
+ if (!cond_.open((std::string{"_waiter_cond_"} + name).c_str())) {
40
+ return false;
41
+ }
42
+ if (!lock_.open((std::string{"_waiter_lock_"} + name).c_str())) {
43
+ cond_.close();
44
+ return false;
45
+ }
46
+ return valid();
47
+ }
48
+
49
+ void close() noexcept {
50
+ cond_.close();
51
+ lock_.close();
52
+ }
53
+
54
+ template <typename F>
55
+ bool wait_if(F &&pred, std::uint64_t tm = ipc::invalid_value) noexcept {
56
+ IPC_UNUSED_ std::lock_guard<ipc::sync::mutex> guard {lock_};
57
+ while ([this, &pred] {
58
+ return !quit_.load(std::memory_order_relaxed)
59
+ && std::forward<F>(pred)();
60
+ }()) {
61
+ if (!cond_.wait(lock_, tm)) return false;
62
+ }
63
+ return true;
64
+ }
65
+
66
+ bool notify() noexcept {
67
+ std::lock_guard<ipc::sync::mutex>{lock_}; // barrier
68
+ return cond_.notify(lock_);
69
+ }
70
+
71
+ bool broadcast() noexcept {
72
+ std::lock_guard<ipc::sync::mutex>{lock_}; // barrier
73
+ return cond_.broadcast(lock_);
74
+ }
75
+
76
+ bool quit_waiting() {
77
+ quit_.store(true, std::memory_order_release);
78
+ return broadcast();
79
+ }
80
+ };
81
+
82
+ } // namespace detail
83
+ } // namespace ipc
crazy_functions/test_project/cpp/cppipc/来源 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ https://github.com/mutouyun/cpp-ipc
2
+
3
+ A high-performance inter-process communication library using shared memory on Linux/Windows.
crazy_functions/test_project/cpp/libJPG/jpgd.cpp ADDED
@@ -0,0 +1,3276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // jpgd.cpp - C++ class for JPEG decompression.
2
+ // Public domain, Rich Geldreich <richgel99@gmail.com>
3
+ // Last updated Apr. 16, 2011
4
+ // Alex Evans: Linear memory allocator (taken from jpge.h).
5
+ //
6
+ // Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2.
7
+ //
8
+ // Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling.
9
+ // Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain"
10
+ // http://vision.ai.uiuc.edu/~dugad/research/dct/index.html
11
+
12
+ #include "jpgd.h"
13
+ #include <string.h>
14
+
15
+ #include <assert.h>
16
+ // BEGIN EPIC MOD
17
+ #define JPGD_ASSERT(x) { assert(x); CA_ASSUME(x); } (void)0
18
+ // END EPIC MOD
19
+
20
+ #ifdef _MSC_VER
21
+ #pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
22
+ #endif
23
+
24
+ // Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling).
25
+ // This is slower, but results in higher quality on images with highly saturated colors.
26
+ #define JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING 1
27
+
28
+ #define JPGD_TRUE (1)
29
+ #define JPGD_FALSE (0)
30
+
31
+ #define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b))
32
+ #define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b))
33
+
34
+ namespace jpgd {
35
+
36
+ static inline void *jpgd_malloc(size_t nSize) { return FMemory::Malloc(nSize); }
37
+ static inline void jpgd_free(void *p) { FMemory::Free(p); }
38
+
39
+ // BEGIN EPIC MOD
40
+ //@UE3 - use UE3 BGRA encoding instead of assuming RGBA
41
+ // stolen from IImageWrapper.h
42
+ enum ERGBFormatJPG
43
+ {
44
+ Invalid = -1,
45
+ RGBA = 0,
46
+ BGRA = 1,
47
+ Gray = 2,
48
+ };
49
+ static ERGBFormatJPG jpg_format;
50
+ // END EPIC MOD
51
+
52
+ // DCT coefficients are stored in this sequence.
53
+ static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
54
+
55
+ enum JPEG_MARKER
56
+ {
57
+ M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8,
58
+ M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC,
59
+ M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7,
60
+ M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF,
61
+ M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0
62
+ };
63
+
64
+ enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 };
65
+
66
+ #define CONST_BITS 13
67
+ #define PASS1_BITS 2
68
+ #define SCALEDONE ((int32)1)
69
+
70
+ #define FIX_0_298631336 ((int32)2446) /* FIX(0.298631336) */
71
+ #define FIX_0_390180644 ((int32)3196) /* FIX(0.390180644) */
72
+ #define FIX_0_541196100 ((int32)4433) /* FIX(0.541196100) */
73
+ #define FIX_0_765366865 ((int32)6270) /* FIX(0.765366865) */
74
+ #define FIX_0_899976223 ((int32)7373) /* FIX(0.899976223) */
75
+ #define FIX_1_175875602 ((int32)9633) /* FIX(1.175875602) */
76
+ #define FIX_1_501321110 ((int32)12299) /* FIX(1.501321110) */
77
+ #define FIX_1_847759065 ((int32)15137) /* FIX(1.847759065) */
78
+ #define FIX_1_961570560 ((int32)16069) /* FIX(1.961570560) */
79
+ #define FIX_2_053119869 ((int32)16819) /* FIX(2.053119869) */
80
+ #define FIX_2_562915447 ((int32)20995) /* FIX(2.562915447) */
81
+ #define FIX_3_072711026 ((int32)25172) /* FIX(3.072711026) */
82
+
83
+ #define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n))
84
+ #define DESCALE_ZEROSHIFT(x,n) (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n))
85
+
86
+ #define MULTIPLY(var, cnst) ((var) * (cnst))
87
+
88
+ #define CLAMP(i) ((static_cast<uint>(i) > 255) ? (((~i) >> 31) & 0xFF) : (i))
89
+
90
+ // Compiler creates a fast path 1D IDCT for X non-zero columns
91
+ template <int NONZERO_COLS>
92
+ struct Row
93
+ {
94
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
95
+ {
96
+ // ACCESS_COL() will be optimized at compile time to either an array access, or 0.
97
+ #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0)
98
+
99
+ const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6);
100
+
101
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
102
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
103
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
104
+
105
+ const int tmp0 = (ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS;
106
+ const int tmp1 = (ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS;
107
+
108
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
109
+
110
+ const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1);
111
+
112
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
113
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
114
+
115
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
116
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
117
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
118
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
119
+
120
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
121
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
122
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
123
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
124
+
125
+ pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS);
126
+ pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS);
127
+ pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS);
128
+ pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS);
129
+ pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS);
130
+ pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS);
131
+ pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS);
132
+ pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS);
133
+ }
134
+ };
135
+
136
+ template <>
137
+ struct Row<0>
138
+ {
139
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
140
+ {
141
+ #ifdef _MSC_VER
142
+ pTemp; pSrc;
143
+ #endif
144
+ }
145
+ };
146
+
147
+ template <>
148
+ struct Row<1>
149
+ {
150
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
151
+ {
152
+ const int dcval = (pSrc[0] << PASS1_BITS);
153
+
154
+ pTemp[0] = dcval;
155
+ pTemp[1] = dcval;
156
+ pTemp[2] = dcval;
157
+ pTemp[3] = dcval;
158
+ pTemp[4] = dcval;
159
+ pTemp[5] = dcval;
160
+ pTemp[6] = dcval;
161
+ pTemp[7] = dcval;
162
+ }
163
+ };
164
+
165
+ // Compiler creates a fast path 1D IDCT for X non-zero rows
166
+ template <int NONZERO_ROWS>
167
+ struct Col
168
+ {
169
+ static void idct(uint8* pDst_ptr, const int* pTemp)
170
+ {
171
+ // ACCESS_ROW() will be optimized at compile time to either an array access, or 0.
172
+ #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0)
173
+
174
+ const int z2 = ACCESS_ROW(2);
175
+ const int z3 = ACCESS_ROW(6);
176
+
177
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
178
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
179
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
180
+
181
+ const int tmp0 = (ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS;
182
+ const int tmp1 = (ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS;
183
+
184
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
185
+
186
+ const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1);
187
+
188
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
189
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
190
+
191
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
192
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
193
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
194
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
195
+
196
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
197
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
198
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
199
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
200
+
201
+ int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3);
202
+ pDst_ptr[8*0] = (uint8)CLAMP(i);
203
+
204
+ i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3);
205
+ pDst_ptr[8*7] = (uint8)CLAMP(i);
206
+
207
+ i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3);
208
+ pDst_ptr[8*1] = (uint8)CLAMP(i);
209
+
210
+ i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3);
211
+ pDst_ptr[8*6] = (uint8)CLAMP(i);
212
+
213
+ i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3);
214
+ pDst_ptr[8*2] = (uint8)CLAMP(i);
215
+
216
+ i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3);
217
+ pDst_ptr[8*5] = (uint8)CLAMP(i);
218
+
219
+ i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3);
220
+ pDst_ptr[8*3] = (uint8)CLAMP(i);
221
+
222
+ i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3);
223
+ pDst_ptr[8*4] = (uint8)CLAMP(i);
224
+ }
225
+ };
226
+
227
+ template <>
228
+ struct Col<1>
229
+ {
230
+ static void idct(uint8* pDst_ptr, const int* pTemp)
231
+ {
232
+ int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3);
233
+ const uint8 dcval_clamped = (uint8)CLAMP(dcval);
234
+ pDst_ptr[0*8] = dcval_clamped;
235
+ pDst_ptr[1*8] = dcval_clamped;
236
+ pDst_ptr[2*8] = dcval_clamped;
237
+ pDst_ptr[3*8] = dcval_clamped;
238
+ pDst_ptr[4*8] = dcval_clamped;
239
+ pDst_ptr[5*8] = dcval_clamped;
240
+ pDst_ptr[6*8] = dcval_clamped;
241
+ pDst_ptr[7*8] = dcval_clamped;
242
+ }
243
+ };
244
+
245
+ static const uint8 s_idct_row_table[] =
246
+ {
247
+ 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0,
248
+ 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0,
249
+ 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0,
250
+ 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0,
251
+ 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2,
252
+ 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2,
253
+ 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4,
254
+ 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8,
255
+ };
256
+
257
+ static const uint8 s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 };
258
+
259
+ void idct(const jpgd_block_t* pSrc_ptr, uint8* pDst_ptr, int block_max_zag)
260
+ {
261
+ JPGD_ASSERT(block_max_zag >= 1);
262
+ JPGD_ASSERT(block_max_zag <= 64);
263
+
264
+ if (block_max_zag == 1)
265
+ {
266
+ int k = ((pSrc_ptr[0] + 4) >> 3) + 128;
267
+ k = CLAMP(k);
268
+ k = k | (k<<8);
269
+ k = k | (k<<16);
270
+
271
+ for (int i = 8; i > 0; i--)
272
+ {
273
+ *(int*)&pDst_ptr[0] = k;
274
+ *(int*)&pDst_ptr[4] = k;
275
+ pDst_ptr += 8;
276
+ }
277
+ return;
278
+ }
279
+
280
+ int temp[64];
281
+
282
+ const jpgd_block_t* pSrc = pSrc_ptr;
283
+ int* pTemp = temp;
284
+
285
+ const uint8* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8];
286
+ int i;
287
+ for (i = 8; i > 0; i--, pRow_tab++)
288
+ {
289
+ switch (*pRow_tab)
290
+ {
291
+ case 0: Row<0>::idct(pTemp, pSrc); break;
292
+ case 1: Row<1>::idct(pTemp, pSrc); break;
293
+ case 2: Row<2>::idct(pTemp, pSrc); break;
294
+ case 3: Row<3>::idct(pTemp, pSrc); break;
295
+ case 4: Row<4>::idct(pTemp, pSrc); break;
296
+ case 5: Row<5>::idct(pTemp, pSrc); break;
297
+ case 6: Row<6>::idct(pTemp, pSrc); break;
298
+ case 7: Row<7>::idct(pTemp, pSrc); break;
299
+ case 8: Row<8>::idct(pTemp, pSrc); break;
300
+ }
301
+
302
+ pSrc += 8;
303
+ pTemp += 8;
304
+ }
305
+
306
+ pTemp = temp;
307
+
308
+ const int nonzero_rows = s_idct_col_table[block_max_zag - 1];
309
+ for (i = 8; i > 0; i--)
310
+ {
311
+ switch (nonzero_rows)
312
+ {
313
+ case 1: Col<1>::idct(pDst_ptr, pTemp); break;
314
+ case 2: Col<2>::idct(pDst_ptr, pTemp); break;
315
+ case 3: Col<3>::idct(pDst_ptr, pTemp); break;
316
+ case 4: Col<4>::idct(pDst_ptr, pTemp); break;
317
+ case 5: Col<5>::idct(pDst_ptr, pTemp); break;
318
+ case 6: Col<6>::idct(pDst_ptr, pTemp); break;
319
+ case 7: Col<7>::idct(pDst_ptr, pTemp); break;
320
+ case 8: Col<8>::idct(pDst_ptr, pTemp); break;
321
+ }
322
+
323
+ pTemp++;
324
+ pDst_ptr++;
325
+ }
326
+ }
327
+
328
+ void idct_4x4(const jpgd_block_t* pSrc_ptr, uint8* pDst_ptr)
329
+ {
330
+ int temp[64];
331
+ int* pTemp = temp;
332
+ const jpgd_block_t* pSrc = pSrc_ptr;
333
+
334
+ for (int i = 4; i > 0; i--)
335
+ {
336
+ Row<4>::idct(pTemp, pSrc);
337
+ pSrc += 8;
338
+ pTemp += 8;
339
+ }
340
+
341
+ pTemp = temp;
342
+ for (int i = 8; i > 0; i--)
343
+ {
344
+ Col<4>::idct(pDst_ptr, pTemp);
345
+ pTemp++;
346
+ pDst_ptr++;
347
+ }
348
+ }
349
+
350
+ // Retrieve one character from the input stream.
351
+ inline uint jpeg_decoder::get_char()
352
+ {
353
+ // Any bytes remaining in buffer?
354
+ if (!m_in_buf_left)
355
+ {
356
+ // Try to get more bytes.
357
+ prep_in_buffer();
358
+ // Still nothing to get?
359
+ if (!m_in_buf_left)
360
+ {
361
+ // Pad the end of the stream with 0xFF 0xD9 (EOI marker)
362
+ int t = m_tem_flag;
363
+ m_tem_flag ^= 1;
364
+ if (t)
365
+ return 0xD9;
366
+ else
367
+ return 0xFF;
368
+ }
369
+ }
370
+
371
+ uint c = *m_pIn_buf_ofs++;
372
+ m_in_buf_left--;
373
+
374
+ return c;
375
+ }
376
+
377
+ // Same as previous method, except can indicate if the character is a pad character or not.
378
+ inline uint jpeg_decoder::get_char(bool *pPadding_flag)
379
+ {
380
+ if (!m_in_buf_left)
381
+ {
382
+ prep_in_buffer();
383
+ if (!m_in_buf_left)
384
+ {
385
+ *pPadding_flag = true;
386
+ int t = m_tem_flag;
387
+ m_tem_flag ^= 1;
388
+ if (t)
389
+ return 0xD9;
390
+ else
391
+ return 0xFF;
392
+ }
393
+ }
394
+
395
+ *pPadding_flag = false;
396
+
397
+ uint c = *m_pIn_buf_ofs++;
398
+ m_in_buf_left--;
399
+
400
+ return c;
401
+ }
402
+
403
+ // Inserts a previously retrieved character back into the input buffer.
404
+ inline void jpeg_decoder::stuff_char(uint8 q)
405
+ {
406
+ *(--m_pIn_buf_ofs) = q;
407
+ m_in_buf_left++;
408
+ }
409
+
410
+ // Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered.
411
+ inline uint8 jpeg_decoder::get_octet()
412
+ {
413
+ bool padding_flag;
414
+ int c = get_char(&padding_flag);
415
+
416
+ if (c == 0xFF)
417
+ {
418
+ if (padding_flag)
419
+ return 0xFF;
420
+
421
+ c = get_char(&padding_flag);
422
+ if (padding_flag)
423
+ {
424
+ stuff_char(0xFF);
425
+ return 0xFF;
426
+ }
427
+
428
+ if (c == 0x00)
429
+ return 0xFF;
430
+ else
431
+ {
432
+ stuff_char(static_cast<uint8>(c));
433
+ stuff_char(0xFF);
434
+ return 0xFF;
435
+ }
436
+ }
437
+
438
+ return static_cast<uint8>(c);
439
+ }
440
+
441
+ // Retrieves a variable number of bits from the input stream. Does not recognize markers.
442
+ inline uint jpeg_decoder::get_bits(int num_bits)
443
+ {
444
+ if (!num_bits)
445
+ return 0;
446
+
447
+ uint i = m_bit_buf >> (32 - num_bits);
448
+
449
+ if ((m_bits_left -= num_bits) <= 0)
450
+ {
451
+ m_bit_buf <<= (num_bits += m_bits_left);
452
+
453
+ uint c1 = get_char();
454
+ uint c2 = get_char();
455
+ m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2;
456
+
457
+ m_bit_buf <<= -m_bits_left;
458
+
459
+ m_bits_left += 16;
460
+
461
+ JPGD_ASSERT(m_bits_left >= 0);
462
+ }
463
+ else
464
+ m_bit_buf <<= num_bits;
465
+
466
+ return i;
467
+ }
468
+
469
+ // Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered.
470
+ inline uint jpeg_decoder::get_bits_no_markers(int num_bits)
471
+ {
472
+ if (!num_bits)
473
+ return 0;
474
+
475
+ uint i = m_bit_buf >> (32 - num_bits);
476
+
477
+ if ((m_bits_left -= num_bits) <= 0)
478
+ {
479
+ m_bit_buf <<= (num_bits += m_bits_left);
480
+
481
+ if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF))
482
+ {
483
+ uint c1 = get_octet();
484
+ uint c2 = get_octet();
485
+ m_bit_buf |= (c1 << 8) | c2;
486
+ }
487
+ else
488
+ {
489
+ m_bit_buf |= ((uint)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1];
490
+ m_in_buf_left -= 2;
491
+ m_pIn_buf_ofs += 2;
492
+ }
493
+
494
+ m_bit_buf <<= -m_bits_left;
495
+
496
+ m_bits_left += 16;
497
+
498
+ JPGD_ASSERT(m_bits_left >= 0);
499
+ }
500
+ else
501
+ m_bit_buf <<= num_bits;
502
+
503
+ return i;
504
+ }
505
+
506
+ // Decodes a Huffman encoded symbol.
507
+ inline int jpeg_decoder::huff_decode(huff_tables *pH)
508
+ {
509
+ int symbol;
510
+
511
+ // Check first 8-bits: do we have a complete symbol?
512
+ if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0)
513
+ {
514
+ // Decode more bits, use a tree traversal to find symbol.
515
+ int ofs = 23;
516
+ do
517
+ {
518
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
519
+ ofs--;
520
+ } while (symbol < 0);
521
+
522
+ get_bits_no_markers(8 + (23 - ofs));
523
+ }
524
+ else
525
+ get_bits_no_markers(pH->code_size[symbol]);
526
+
527
+ return symbol;
528
+ }
529
+
530
+ // Decodes a Huffman encoded symbol.
531
+ inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
532
+ {
533
+ int symbol;
534
+
535
+ // Check first 8-bits: do we have a complete symbol?
536
+ if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0)
537
+ {
538
+ // Use a tree traversal to find symbol.
539
+ int ofs = 23;
540
+ do
541
+ {
542
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
543
+ ofs--;
544
+ } while (symbol < 0);
545
+
546
+ get_bits_no_markers(8 + (23 - ofs));
547
+
548
+ extra_bits = get_bits_no_markers(symbol & 0xF);
549
+ }
550
+ else
551
+ {
552
+ JPGD_ASSERT(((symbol >> 8) & 31) == pH->code_size[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0));
553
+
554
+ if (symbol & 0x8000)
555
+ {
556
+ get_bits_no_markers((symbol >> 8) & 31);
557
+ extra_bits = symbol >> 16;
558
+ }
559
+ else
560
+ {
561
+ int code_size = (symbol >> 8) & 31;
562
+ int num_extra_bits = symbol & 0xF;
563
+ int bits = code_size + num_extra_bits;
564
+ if (bits <= (m_bits_left + 16))
565
+ extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1);
566
+ else
567
+ {
568
+ get_bits_no_markers(code_size);
569
+ extra_bits = get_bits_no_markers(num_extra_bits);
570
+ }
571
+ }
572
+
573
+ symbol &= 0xFF;
574
+ }
575
+
576
+ return symbol;
577
+ }
578
+
579
+ // Tables and macro used to fully decode the DPCM differences.
580
+ static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
581
+ static const int s_extend_offset[16] = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 };
582
+ static const int s_extend_mask[] = { 0, (1<<0), (1<<1), (1<<2), (1<<3), (1<<4), (1<<5), (1<<6), (1<<7), (1<<8), (1<<9), (1<<10), (1<<11), (1<<12), (1<<13), (1<<14), (1<<15), (1<<16) };
583
+ #define HUFF_EXTEND(x,s) ((x) < s_extend_test[s] ? (x) + s_extend_offset[s] : (x))
584
+
585
+ // Clamps a value between 0-255.
586
+ inline uint8 jpeg_decoder::clamp(int i)
587
+ {
588
+ if (static_cast<uint>(i) > 255)
589
+ i = (((~i) >> 31) & 0xFF);
590
+
591
+ return static_cast<uint8>(i);
592
+ }
593
+
594
+ namespace DCT_Upsample
595
+ {
596
+ struct Matrix44
597
+ {
598
+ typedef int Element_Type;
599
+ enum { NUM_ROWS = 4, NUM_COLS = 4 };
600
+
601
+ Element_Type v[NUM_ROWS][NUM_COLS];
602
+
603
+ inline int rows() const { return NUM_ROWS; }
604
+ inline int cols() const { return NUM_COLS; }
605
+
606
+ inline const Element_Type & at(int r, int c) const { return v[r][c]; }
607
+ inline Element_Type & at(int r, int c) { return v[r][c]; }
608
+
609
+ inline Matrix44() { }
610
+
611
+ inline Matrix44& operator += (const Matrix44& a)
612
+ {
613
+ for (int r = 0; r < NUM_ROWS; r++)
614
+ {
615
+ at(r, 0) += a.at(r, 0);
616
+ at(r, 1) += a.at(r, 1);
617
+ at(r, 2) += a.at(r, 2);
618
+ at(r, 3) += a.at(r, 3);
619
+ }
620
+ return *this;
621
+ }
622
+
623
+ inline Matrix44& operator -= (const Matrix44& a)
624
+ {
625
+ for (int r = 0; r < NUM_ROWS; r++)
626
+ {
627
+ at(r, 0) -= a.at(r, 0);
628
+ at(r, 1) -= a.at(r, 1);
629
+ at(r, 2) -= a.at(r, 2);
630
+ at(r, 3) -= a.at(r, 3);
631
+ }
632
+ return *this;
633
+ }
634
+
635
+ friend inline Matrix44 operator + (const Matrix44& a, const Matrix44& b)
636
+ {
637
+ Matrix44 ret;
638
+ for (int r = 0; r < NUM_ROWS; r++)
639
+ {
640
+ ret.at(r, 0) = a.at(r, 0) + b.at(r, 0);
641
+ ret.at(r, 1) = a.at(r, 1) + b.at(r, 1);
642
+ ret.at(r, 2) = a.at(r, 2) + b.at(r, 2);
643
+ ret.at(r, 3) = a.at(r, 3) + b.at(r, 3);
644
+ }
645
+ return ret;
646
+ }
647
+
648
+ friend inline Matrix44 operator - (const Matrix44& a, const Matrix44& b)
649
+ {
650
+ Matrix44 ret;
651
+ for (int r = 0; r < NUM_ROWS; r++)
652
+ {
653
+ ret.at(r, 0) = a.at(r, 0) - b.at(r, 0);
654
+ ret.at(r, 1) = a.at(r, 1) - b.at(r, 1);
655
+ ret.at(r, 2) = a.at(r, 2) - b.at(r, 2);
656
+ ret.at(r, 3) = a.at(r, 3) - b.at(r, 3);
657
+ }
658
+ return ret;
659
+ }
660
+
661
+ static inline void add_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
662
+ {
663
+ for (int r = 0; r < 4; r++)
664
+ {
665
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) + b.at(r, 0));
666
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) + b.at(r, 1));
667
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) + b.at(r, 2));
668
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) + b.at(r, 3));
669
+ }
670
+ }
671
+
672
+ static inline void sub_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
673
+ {
674
+ for (int r = 0; r < 4; r++)
675
+ {
676
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) - b.at(r, 0));
677
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) - b.at(r, 1));
678
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) - b.at(r, 2));
679
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) - b.at(r, 3));
680
+ }
681
+ }
682
+ };
683
+
684
+ const int FRACT_BITS = 10;
685
+ const int SCALE = 1 << FRACT_BITS;
686
+
687
+ typedef int Temp_Type;
688
+ #define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS)
689
+ #define F(i) ((int)((i) * SCALE + .5f))
690
+
691
+ // Any decent C++ compiler will optimize this at compile time to a 0, or an array access.
692
+ #define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8])
693
+
694
+ // NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix
695
+ template<int NUM_ROWS, int NUM_COLS>
696
+ struct P_Q
697
+ {
698
+ static void calc(Matrix44& P, Matrix44& Q, const jpgd_block_t* pSrc)
699
+ {
700
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
701
+ const Temp_Type X000 = AT(0, 0);
702
+ const Temp_Type X001 = AT(0, 1);
703
+ const Temp_Type X002 = AT(0, 2);
704
+ const Temp_Type X003 = AT(0, 3);
705
+ const Temp_Type X004 = AT(0, 4);
706
+ const Temp_Type X005 = AT(0, 5);
707
+ const Temp_Type X006 = AT(0, 6);
708
+ const Temp_Type X007 = AT(0, 7);
709
+ const Temp_Type X010 = D(F(0.415735f) * AT(1, 0) + F(0.791065f) * AT(3, 0) + F(-0.352443f) * AT(5, 0) + F(0.277785f) * AT(7, 0));
710
+ const Temp_Type X011 = D(F(0.415735f) * AT(1, 1) + F(0.791065f) * AT(3, 1) + F(-0.352443f) * AT(5, 1) + F(0.277785f) * AT(7, 1));
711
+ const Temp_Type X012 = D(F(0.415735f) * AT(1, 2) + F(0.791065f) * AT(3, 2) + F(-0.352443f) * AT(5, 2) + F(0.277785f) * AT(7, 2));
712
+ const Temp_Type X013 = D(F(0.415735f) * AT(1, 3) + F(0.791065f) * AT(3, 3) + F(-0.352443f) * AT(5, 3) + F(0.277785f) * AT(7, 3));
713
+ const Temp_Type X014 = D(F(0.415735f) * AT(1, 4) + F(0.791065f) * AT(3, 4) + F(-0.352443f) * AT(5, 4) + F(0.277785f) * AT(7, 4));
714
+ const Temp_Type X015 = D(F(0.415735f) * AT(1, 5) + F(0.791065f) * AT(3, 5) + F(-0.352443f) * AT(5, 5) + F(0.277785f) * AT(7, 5));
715
+ const Temp_Type X016 = D(F(0.415735f) * AT(1, 6) + F(0.791065f) * AT(3, 6) + F(-0.352443f) * AT(5, 6) + F(0.277785f) * AT(7, 6));
716
+ const Temp_Type X017 = D(F(0.415735f) * AT(1, 7) + F(0.791065f) * AT(3, 7) + F(-0.352443f) * AT(5, 7) + F(0.277785f) * AT(7, 7));
717
+ const Temp_Type X020 = AT(4, 0);
718
+ const Temp_Type X021 = AT(4, 1);
719
+ const Temp_Type X022 = AT(4, 2);
720
+ const Temp_Type X023 = AT(4, 3);
721
+ const Temp_Type X024 = AT(4, 4);
722
+ const Temp_Type X025 = AT(4, 5);
723
+ const Temp_Type X026 = AT(4, 6);
724
+ const Temp_Type X027 = AT(4, 7);
725
+ const Temp_Type X030 = D(F(0.022887f) * AT(1, 0) + F(-0.097545f) * AT(3, 0) + F(0.490393f) * AT(5, 0) + F(0.865723f) * AT(7, 0));
726
+ const Temp_Type X031 = D(F(0.022887f) * AT(1, 1) + F(-0.097545f) * AT(3, 1) + F(0.490393f) * AT(5, 1) + F(0.865723f) * AT(7, 1));
727
+ const Temp_Type X032 = D(F(0.022887f) * AT(1, 2) + F(-0.097545f) * AT(3, 2) + F(0.490393f) * AT(5, 2) + F(0.865723f) * AT(7, 2));
728
+ const Temp_Type X033 = D(F(0.022887f) * AT(1, 3) + F(-0.097545f) * AT(3, 3) + F(0.490393f) * AT(5, 3) + F(0.865723f) * AT(7, 3));
729
+ const Temp_Type X034 = D(F(0.022887f) * AT(1, 4) + F(-0.097545f) * AT(3, 4) + F(0.490393f) * AT(5, 4) + F(0.865723f) * AT(7, 4));
730
+ const Temp_Type X035 = D(F(0.022887f) * AT(1, 5) + F(-0.097545f) * AT(3, 5) + F(0.490393f) * AT(5, 5) + F(0.865723f) * AT(7, 5));
731
+ const Temp_Type X036 = D(F(0.022887f) * AT(1, 6) + F(-0.097545f) * AT(3, 6) + F(0.490393f) * AT(5, 6) + F(0.865723f) * AT(7, 6));
732
+ const Temp_Type X037 = D(F(0.022887f) * AT(1, 7) + F(-0.097545f) * AT(3, 7) + F(0.490393f) * AT(5, 7) + F(0.865723f) * AT(7, 7));
733
+
734
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
735
+ P.at(0, 0) = X000;
736
+ P.at(0, 1) = D(X001 * F(0.415735f) + X003 * F(0.791065f) + X005 * F(-0.352443f) + X007 * F(0.277785f));
737
+ P.at(0, 2) = X004;
738
+ P.at(0, 3) = D(X001 * F(0.022887f) + X003 * F(-0.097545f) + X005 * F(0.490393f) + X007 * F(0.865723f));
739
+ P.at(1, 0) = X010;
740
+ P.at(1, 1) = D(X011 * F(0.415735f) + X013 * F(0.791065f) + X015 * F(-0.352443f) + X017 * F(0.277785f));
741
+ P.at(1, 2) = X014;
742
+ P.at(1, 3) = D(X011 * F(0.022887f) + X013 * F(-0.097545f) + X015 * F(0.490393f) + X017 * F(0.865723f));
743
+ P.at(2, 0) = X020;
744
+ P.at(2, 1) = D(X021 * F(0.415735f) + X023 * F(0.791065f) + X025 * F(-0.352443f) + X027 * F(0.277785f));
745
+ P.at(2, 2) = X024;
746
+ P.at(2, 3) = D(X021 * F(0.022887f) + X023 * F(-0.097545f) + X025 * F(0.490393f) + X027 * F(0.865723f));
747
+ P.at(3, 0) = X030;
748
+ P.at(3, 1) = D(X031 * F(0.415735f) + X033 * F(0.791065f) + X035 * F(-0.352443f) + X037 * F(0.277785f));
749
+ P.at(3, 2) = X034;
750
+ P.at(3, 3) = D(X031 * F(0.022887f) + X033 * F(-0.097545f) + X035 * F(0.490393f) + X037 * F(0.865723f));
751
+ // 40 muls 24 adds
752
+
753
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
754
+ Q.at(0, 0) = D(X001 * F(0.906127f) + X003 * F(-0.318190f) + X005 * F(0.212608f) + X007 * F(-0.180240f));
755
+ Q.at(0, 1) = X002;
756
+ Q.at(0, 2) = D(X001 * F(-0.074658f) + X003 * F(0.513280f) + X005 * F(0.768178f) + X007 * F(-0.375330f));
757
+ Q.at(0, 3) = X006;
758
+ Q.at(1, 0) = D(X011 * F(0.906127f) + X013 * F(-0.318190f) + X015 * F(0.212608f) + X017 * F(-0.180240f));
759
+ Q.at(1, 1) = X012;
760
+ Q.at(1, 2) = D(X011 * F(-0.074658f) + X013 * F(0.513280f) + X015 * F(0.768178f) + X017 * F(-0.375330f));
761
+ Q.at(1, 3) = X016;
762
+ Q.at(2, 0) = D(X021 * F(0.906127f) + X023 * F(-0.318190f) + X025 * F(0.212608f) + X027 * F(-0.180240f));
763
+ Q.at(2, 1) = X022;
764
+ Q.at(2, 2) = D(X021 * F(-0.074658f) + X023 * F(0.513280f) + X025 * F(0.768178f) + X027 * F(-0.375330f));
765
+ Q.at(2, 3) = X026;
766
+ Q.at(3, 0) = D(X031 * F(0.906127f) + X033 * F(-0.318190f) + X035 * F(0.212608f) + X037 * F(-0.180240f));
767
+ Q.at(3, 1) = X032;
768
+ Q.at(3, 2) = D(X031 * F(-0.074658f) + X033 * F(0.513280f) + X035 * F(0.768178f) + X037 * F(-0.375330f));
769
+ Q.at(3, 3) = X036;
770
+ // 40 muls 24 adds
771
+ }
772
+ };
773
+
774
+ template<int NUM_ROWS, int NUM_COLS>
775
+ struct R_S
776
+ {
777
+ static void calc(Matrix44& R, Matrix44& S, const jpgd_block_t* pSrc)
778
+ {
779
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
780
+ const Temp_Type X100 = D(F(0.906127f) * AT(1, 0) + F(-0.318190f) * AT(3, 0) + F(0.212608f) * AT(5, 0) + F(-0.180240f) * AT(7, 0));
781
+ const Temp_Type X101 = D(F(0.906127f) * AT(1, 1) + F(-0.318190f) * AT(3, 1) + F(0.212608f) * AT(5, 1) + F(-0.180240f) * AT(7, 1));
782
+ const Temp_Type X102 = D(F(0.906127f) * AT(1, 2) + F(-0.318190f) * AT(3, 2) + F(0.212608f) * AT(5, 2) + F(-0.180240f) * AT(7, 2));
783
+ const Temp_Type X103 = D(F(0.906127f) * AT(1, 3) + F(-0.318190f) * AT(3, 3) + F(0.212608f) * AT(5, 3) + F(-0.180240f) * AT(7, 3));
784
+ const Temp_Type X104 = D(F(0.906127f) * AT(1, 4) + F(-0.318190f) * AT(3, 4) + F(0.212608f) * AT(5, 4) + F(-0.180240f) * AT(7, 4));
785
+ const Temp_Type X105 = D(F(0.906127f) * AT(1, 5) + F(-0.318190f) * AT(3, 5) + F(0.212608f) * AT(5, 5) + F(-0.180240f) * AT(7, 5));
786
+ const Temp_Type X106 = D(F(0.906127f) * AT(1, 6) + F(-0.318190f) * AT(3, 6) + F(0.212608f) * AT(5, 6) + F(-0.180240f) * AT(7, 6));
787
+ const Temp_Type X107 = D(F(0.906127f) * AT(1, 7) + F(-0.318190f) * AT(3, 7) + F(0.212608f) * AT(5, 7) + F(-0.180240f) * AT(7, 7));
788
+ const Temp_Type X110 = AT(2, 0);
789
+ const Temp_Type X111 = AT(2, 1);
790
+ const Temp_Type X112 = AT(2, 2);
791
+ const Temp_Type X113 = AT(2, 3);
792
+ const Temp_Type X114 = AT(2, 4);
793
+ const Temp_Type X115 = AT(2, 5);
794
+ const Temp_Type X116 = AT(2, 6);
795
+ const Temp_Type X117 = AT(2, 7);
796
+ const Temp_Type X120 = D(F(-0.074658f) * AT(1, 0) + F(0.513280f) * AT(3, 0) + F(0.768178f) * AT(5, 0) + F(-0.375330f) * AT(7, 0));
797
+ const Temp_Type X121 = D(F(-0.074658f) * AT(1, 1) + F(0.513280f) * AT(3, 1) + F(0.768178f) * AT(5, 1) + F(-0.375330f) * AT(7, 1));
798
+ const Temp_Type X122 = D(F(-0.074658f) * AT(1, 2) + F(0.513280f) * AT(3, 2) + F(0.768178f) * AT(5, 2) + F(-0.375330f) * AT(7, 2));
799
+ const Temp_Type X123 = D(F(-0.074658f) * AT(1, 3) + F(0.513280f) * AT(3, 3) + F(0.768178f) * AT(5, 3) + F(-0.375330f) * AT(7, 3));
800
+ const Temp_Type X124 = D(F(-0.074658f) * AT(1, 4) + F(0.513280f) * AT(3, 4) + F(0.768178f) * AT(5, 4) + F(-0.375330f) * AT(7, 4));
801
+ const Temp_Type X125 = D(F(-0.074658f) * AT(1, 5) + F(0.513280f) * AT(3, 5) + F(0.768178f) * AT(5, 5) + F(-0.375330f) * AT(7, 5));
802
+ const Temp_Type X126 = D(F(-0.074658f) * AT(1, 6) + F(0.513280f) * AT(3, 6) + F(0.768178f) * AT(5, 6) + F(-0.375330f) * AT(7, 6));
803
+ const Temp_Type X127 = D(F(-0.074658f) * AT(1, 7) + F(0.513280f) * AT(3, 7) + F(0.768178f) * AT(5, 7) + F(-0.375330f) * AT(7, 7));
804
+ const Temp_Type X130 = AT(6, 0);
805
+ const Temp_Type X131 = AT(6, 1);
806
+ const Temp_Type X132 = AT(6, 2);
807
+ const Temp_Type X133 = AT(6, 3);
808
+ const Temp_Type X134 = AT(6, 4);
809
+ const Temp_Type X135 = AT(6, 5);
810
+ const Temp_Type X136 = AT(6, 6);
811
+ const Temp_Type X137 = AT(6, 7);
812
+ // 80 muls 48 adds
813
+
814
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
815
+ R.at(0, 0) = X100;
816
+ R.at(0, 1) = D(X101 * F(0.415735f) + X103 * F(0.791065f) + X105 * F(-0.352443f) + X107 * F(0.277785f));
817
+ R.at(0, 2) = X104;
818
+ R.at(0, 3) = D(X101 * F(0.022887f) + X103 * F(-0.097545f) + X105 * F(0.490393f) + X107 * F(0.865723f));
819
+ R.at(1, 0) = X110;
820
+ R.at(1, 1) = D(X111 * F(0.415735f) + X113 * F(0.791065f) + X115 * F(-0.352443f) + X117 * F(0.277785f));
821
+ R.at(1, 2) = X114;
822
+ R.at(1, 3) = D(X111 * F(0.022887f) + X113 * F(-0.097545f) + X115 * F(0.490393f) + X117 * F(0.865723f));
823
+ R.at(2, 0) = X120;
824
+ R.at(2, 1) = D(X121 * F(0.415735f) + X123 * F(0.791065f) + X125 * F(-0.352443f) + X127 * F(0.277785f));
825
+ R.at(2, 2) = X124;
826
+ R.at(2, 3) = D(X121 * F(0.022887f) + X123 * F(-0.097545f) + X125 * F(0.490393f) + X127 * F(0.865723f));
827
+ R.at(3, 0) = X130;
828
+ R.at(3, 1) = D(X131 * F(0.415735f) + X133 * F(0.791065f) + X135 * F(-0.352443f) + X137 * F(0.277785f));
829
+ R.at(3, 2) = X134;
830
+ R.at(3, 3) = D(X131 * F(0.022887f) + X133 * F(-0.097545f) + X135 * F(0.490393f) + X137 * F(0.865723f));
831
+ // 40 muls 24 adds
832
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
833
+ S.at(0, 0) = D(X101 * F(0.906127f) + X103 * F(-0.318190f) + X105 * F(0.212608f) + X107 * F(-0.180240f));
834
+ S.at(0, 1) = X102;
835
+ S.at(0, 2) = D(X101 * F(-0.074658f) + X103 * F(0.513280f) + X105 * F(0.768178f) + X107 * F(-0.375330f));
836
+ S.at(0, 3) = X106;
837
+ S.at(1, 0) = D(X111 * F(0.906127f) + X113 * F(-0.318190f) + X115 * F(0.212608f) + X117 * F(-0.180240f));
838
+ S.at(1, 1) = X112;
839
+ S.at(1, 2) = D(X111 * F(-0.074658f) + X113 * F(0.513280f) + X115 * F(0.768178f) + X117 * F(-0.375330f));
840
+ S.at(1, 3) = X116;
841
+ S.at(2, 0) = D(X121 * F(0.906127f) + X123 * F(-0.318190f) + X125 * F(0.212608f) + X127 * F(-0.180240f));
842
+ S.at(2, 1) = X122;
843
+ S.at(2, 2) = D(X121 * F(-0.074658f) + X123 * F(0.513280f) + X125 * F(0.768178f) + X127 * F(-0.375330f));
844
+ S.at(2, 3) = X126;
845
+ S.at(3, 0) = D(X131 * F(0.906127f) + X133 * F(-0.318190f) + X135 * F(0.212608f) + X137 * F(-0.180240f));
846
+ S.at(3, 1) = X132;
847
+ S.at(3, 2) = D(X131 * F(-0.074658f) + X133 * F(0.513280f) + X135 * F(0.768178f) + X137 * F(-0.375330f));
848
+ S.at(3, 3) = X136;
849
+ // 40 muls 24 adds
850
+ }
851
+ };
852
+ } // end namespace DCT_Upsample
853
+
854
+ // Unconditionally frees all allocated m_blocks.
855
+ void jpeg_decoder::free_all_blocks()
856
+ {
857
+ m_pStream = NULL;
858
+ for (mem_block *b = m_pMem_blocks; b; )
859
+ {
860
+ mem_block *n = b->m_pNext;
861
+ jpgd_free(b);
862
+ b = n;
863
+ }
864
+ m_pMem_blocks = NULL;
865
+ }
866
+
867
+ // This method handles all errors.
868
+ // It could easily be changed to use C++ exceptions.
869
+ void jpeg_decoder::stop_decoding(jpgd_status status)
870
+ {
871
+ m_error_code = status;
872
+ free_all_blocks();
873
+ longjmp(m_jmp_state, status);
874
+
875
+ // we shouldn't get here as longjmp shouldn't return, but we put it here to make it explicit
876
+ // that this function doesn't return, otherwise we get this error:
877
+ //
878
+ // error : function declared 'noreturn' should not return
879
+ exit(1);
880
+ }
881
+
882
+ void *jpeg_decoder::alloc(size_t nSize, bool zero)
883
+ {
884
+ nSize = (JPGD_MAX(nSize, 1) + 3) & ~3;
885
+ char *rv = NULL;
886
+ for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext)
887
+ {
888
+ if ((b->m_used_count + nSize) <= b->m_size)
889
+ {
890
+ rv = b->m_data + b->m_used_count;
891
+ b->m_used_count += nSize;
892
+ break;
893
+ }
894
+ }
895
+ if (!rv)
896
+ {
897
+ int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
898
+ mem_block *b = (mem_block*)jpgd_malloc(sizeof(mem_block) + capacity);
899
+ if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
900
+ b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
901
+ b->m_used_count = nSize;
902
+ b->m_size = capacity;
903
+ rv = b->m_data;
904
+ }
905
+ if (zero) memset(rv, 0, nSize);
906
+ return rv;
907
+ }
908
+
909
+ void jpeg_decoder::word_clear(void *p, uint16 c, uint n)
910
+ {
911
+ uint8 *pD = (uint8*)p;
912
+ const uint8 l = c & 0xFF, h = (c >> 8) & 0xFF;
913
+ while (n)
914
+ {
915
+ pD[0] = l; pD[1] = h; pD += 2;
916
+ n--;
917
+ }
918
+ }
919
+
920
+ // Refill the input buffer.
921
+ // This method will sit in a loop until (A) the buffer is full or (B)
922
+ // the stream's read() method reports and end of file condition.
923
+ void jpeg_decoder::prep_in_buffer()
924
+ {
925
+ m_in_buf_left = 0;
926
+ m_pIn_buf_ofs = m_in_buf;
927
+
928
+ if (m_eof_flag)
929
+ return;
930
+
931
+ do
932
+ {
933
+ int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
934
+ if (bytes_read == -1)
935
+ stop_decoding(JPGD_STREAM_READ);
936
+
937
+ m_in_buf_left += bytes_read;
938
+ } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
939
+
940
+ m_total_bytes_read += m_in_buf_left;
941
+
942
+ // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
943
+ // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
944
+ word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
945
+ }
946
+
947
+ // Read a Huffman code table.
948
+ void jpeg_decoder::read_dht_marker()
949
+ {
950
+ int i, index, count;
951
+ uint8 huff_num[17];
952
+ uint8 huff_val[256];
953
+
954
+ uint num_left = get_bits(16);
955
+
956
+ if (num_left < 2)
957
+ stop_decoding(JPGD_BAD_DHT_MARKER);
958
+
959
+ num_left -= 2;
960
+
961
+ while (num_left)
962
+ {
963
+ index = get_bits(8);
964
+
965
+ huff_num[0] = 0;
966
+
967
+ count = 0;
968
+
969
+ for (i = 1; i <= 16; i++)
970
+ {
971
+ huff_num[i] = static_cast<uint8>(get_bits(8));
972
+ count += huff_num[i];
973
+ }
974
+
975
+ if (count > 255)
976
+ stop_decoding(JPGD_BAD_DHT_COUNTS);
977
+
978
+ for (i = 0; i < count; i++)
979
+ huff_val[i] = static_cast<uint8>(get_bits(8));
980
+
981
+ i = 1 + 16 + count;
982
+
983
+ if (num_left < (uint)i)
984
+ stop_decoding(JPGD_BAD_DHT_MARKER);
985
+
986
+ num_left -= i;
987
+
988
+ if ((index & 0x10) > 0x10)
989
+ stop_decoding(JPGD_BAD_DHT_INDEX);
990
+
991
+ index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
992
+
993
+ if (index >= JPGD_MAX_HUFF_TABLES)
994
+ stop_decoding(JPGD_BAD_DHT_INDEX);
995
+
996
+ if (!m_huff_num[index])
997
+ m_huff_num[index] = (uint8 *)alloc(17);
998
+
999
+ if (!m_huff_val[index])
1000
+ m_huff_val[index] = (uint8 *)alloc(256);
1001
+
1002
+ m_huff_ac[index] = (index & 0x10) != 0;
1003
+ memcpy(m_huff_num[index], huff_num, 17);
1004
+ memcpy(m_huff_val[index], huff_val, 256);
1005
+ }
1006
+ }
1007
+
1008
+ // Read a quantization table.
1009
+ void jpeg_decoder::read_dqt_marker()
1010
+ {
1011
+ int n, i, prec;
1012
+ uint num_left;
1013
+ uint temp;
1014
+
1015
+ num_left = get_bits(16);
1016
+
1017
+ if (num_left < 2)
1018
+ stop_decoding(JPGD_BAD_DQT_MARKER);
1019
+
1020
+ num_left -= 2;
1021
+
1022
+ while (num_left)
1023
+ {
1024
+ n = get_bits(8);
1025
+ prec = n >> 4;
1026
+ n &= 0x0F;
1027
+
1028
+ if (n >= JPGD_MAX_QUANT_TABLES)
1029
+ stop_decoding(JPGD_BAD_DQT_TABLE);
1030
+
1031
+ if (!m_quant[n])
1032
+ m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t));
1033
+
1034
+ // read quantization entries, in zag order
1035
+ for (i = 0; i < 64; i++)
1036
+ {
1037
+ temp = get_bits(8);
1038
+
1039
+ if (prec)
1040
+ temp = (temp << 8) + get_bits(8);
1041
+
1042
+ m_quant[n][i] = static_cast<jpgd_quant_t>(temp);
1043
+ }
1044
+
1045
+ i = 64 + 1;
1046
+
1047
+ if (prec)
1048
+ i += 64;
1049
+
1050
+ if (num_left < (uint)i)
1051
+ stop_decoding(JPGD_BAD_DQT_LENGTH);
1052
+
1053
+ num_left -= i;
1054
+ }
1055
+ }
1056
+
1057
+ // Read the start of frame (SOF) marker.
1058
+ void jpeg_decoder::read_sof_marker()
1059
+ {
1060
+ int i;
1061
+ uint num_left;
1062
+
1063
+ num_left = get_bits(16);
1064
+
1065
+ if (get_bits(8) != 8) /* precision: sorry, only 8-bit precision is supported right now */
1066
+ stop_decoding(JPGD_BAD_PRECISION);
1067
+
1068
+ m_image_y_size = get_bits(16);
1069
+
1070
+ if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT))
1071
+ stop_decoding(JPGD_BAD_HEIGHT);
1072
+
1073
+ m_image_x_size = get_bits(16);
1074
+
1075
+ if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH))
1076
+ stop_decoding(JPGD_BAD_WIDTH);
1077
+
1078
+ m_comps_in_frame = get_bits(8);
1079
+
1080
+ if (m_comps_in_frame > JPGD_MAX_COMPONENTS)
1081
+ stop_decoding(JPGD_TOO_MANY_COMPONENTS);
1082
+
1083
+ if (num_left != (uint)(m_comps_in_frame * 3 + 8))
1084
+ stop_decoding(JPGD_BAD_SOF_LENGTH);
1085
+
1086
+ for (i = 0; i < m_comps_in_frame; i++)
1087
+ {
1088
+ m_comp_ident[i] = get_bits(8);
1089
+ m_comp_h_samp[i] = get_bits(4);
1090
+ m_comp_v_samp[i] = get_bits(4);
1091
+ m_comp_quant[i] = get_bits(8);
1092
+ }
1093
+ }
1094
+
1095
+ // Used to skip unrecognized markers.
1096
+ void jpeg_decoder::skip_variable_marker()
1097
+ {
1098
+ uint num_left;
1099
+
1100
+ num_left = get_bits(16);
1101
+
1102
+ if (num_left < 2)
1103
+ stop_decoding(JPGD_BAD_VARIABLE_MARKER);
1104
+
1105
+ num_left -= 2;
1106
+
1107
+ while (num_left)
1108
+ {
1109
+ get_bits(8);
1110
+ num_left--;
1111
+ }
1112
+ }
1113
+
1114
+ // Read a define restart interval (DRI) marker.
1115
+ void jpeg_decoder::read_dri_marker()
1116
+ {
1117
+ if (get_bits(16) != 4)
1118
+ stop_decoding(JPGD_BAD_DRI_LENGTH);
1119
+
1120
+ m_restart_interval = get_bits(16);
1121
+ }
1122
+
1123
+ // Read a start of scan (SOS) marker.
1124
+ void jpeg_decoder::read_sos_marker()
1125
+ {
1126
+ uint num_left;
1127
+ int i, ci, n, c, cc;
1128
+
1129
+ num_left = get_bits(16);
1130
+
1131
+ n = get_bits(8);
1132
+
1133
+ m_comps_in_scan = n;
1134
+
1135
+ num_left -= 3;
1136
+
1137
+ if ( (num_left != (uint)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) )
1138
+ stop_decoding(JPGD_BAD_SOS_LENGTH);
1139
+
1140
+ for (i = 0; i < n; i++)
1141
+ {
1142
+ cc = get_bits(8);
1143
+ c = get_bits(8);
1144
+ num_left -= 2;
1145
+
1146
+ for (ci = 0; ci < m_comps_in_frame; ci++)
1147
+ if (cc == m_comp_ident[ci])
1148
+ break;
1149
+
1150
+ if (ci >= m_comps_in_frame)
1151
+ stop_decoding(JPGD_BAD_SOS_COMP_ID);
1152
+
1153
+ m_comp_list[i] = ci;
1154
+ m_comp_dc_tab[ci] = (c >> 4) & 15;
1155
+ m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1);
1156
+ }
1157
+
1158
+ m_spectral_start = get_bits(8);
1159
+ m_spectral_end = get_bits(8);
1160
+ m_successive_high = get_bits(4);
1161
+ m_successive_low = get_bits(4);
1162
+
1163
+ if (!m_progressive_flag)
1164
+ {
1165
+ m_spectral_start = 0;
1166
+ m_spectral_end = 63;
1167
+ }
1168
+
1169
+ num_left -= 3;
1170
+
1171
+ while (num_left) /* read past whatever is num_left */
1172
+ {
1173
+ get_bits(8);
1174
+ num_left--;
1175
+ }
1176
+ }
1177
+
1178
+ // Finds the next marker.
1179
+ int jpeg_decoder::next_marker()
1180
+ {
1181
+ uint c, bytes;
1182
+
1183
+ bytes = 0;
1184
+
1185
+ do
1186
+ {
1187
+ do
1188
+ {
1189
+ bytes++;
1190
+ c = get_bits(8);
1191
+ } while (c != 0xFF);
1192
+
1193
+ do
1194
+ {
1195
+ c = get_bits(8);
1196
+ } while (c == 0xFF);
1197
+
1198
+ } while (c == 0);
1199
+
1200
+ // If bytes > 0 here, there where extra bytes before the marker (not good).
1201
+
1202
+ return c;
1203
+ }
1204
+
1205
+ // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
1206
+ // encountered.
1207
+ int jpeg_decoder::process_markers()
1208
+ {
1209
+ int c;
1210
+
1211
+ for ( ; ; )
1212
+ {
1213
+ c = next_marker();
1214
+
1215
+ switch (c)
1216
+ {
1217
+ case M_SOF0:
1218
+ case M_SOF1:
1219
+ case M_SOF2:
1220
+ case M_SOF3:
1221
+ case M_SOF5:
1222
+ case M_SOF6:
1223
+ case M_SOF7:
1224
+ // case M_JPG:
1225
+ case M_SOF9:
1226
+ case M_SOF10:
1227
+ case M_SOF11:
1228
+ case M_SOF13:
1229
+ case M_SOF14:
1230
+ case M_SOF15:
1231
+ case M_SOI:
1232
+ case M_EOI:
1233
+ case M_SOS:
1234
+ {
1235
+ return c;
1236
+ }
1237
+ case M_DHT:
1238
+ {
1239
+ read_dht_marker();
1240
+ break;
1241
+ }
1242
+ // No arithmitic support - dumb patents!
1243
+ case M_DAC:
1244
+ {
1245
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
1246
+ break;
1247
+ }
1248
+ case M_DQT:
1249
+ {
1250
+ read_dqt_marker();
1251
+ break;
1252
+ }
1253
+ case M_DRI:
1254
+ {
1255
+ read_dri_marker();
1256
+ break;
1257
+ }
1258
+ //case M_APP0: /* no need to read the JFIF marker */
1259
+
1260
+ case M_JPG:
1261
+ case M_RST0: /* no parameters */
1262
+ case M_RST1:
1263
+ case M_RST2:
1264
+ case M_RST3:
1265
+ case M_RST4:
1266
+ case M_RST5:
1267
+ case M_RST6:
1268
+ case M_RST7:
1269
+ case M_TEM:
1270
+ {
1271
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
1272
+ break;
1273
+ }
1274
+ default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
1275
+ {
1276
+ skip_variable_marker();
1277
+ break;
1278
+ }
1279
+ }
1280
+ }
1281
+ }
1282
+
1283
+ // Finds the start of image (SOI) marker.
1284
+ // This code is rather defensive: it only checks the first 512 bytes to avoid
1285
+ // false positives.
1286
+ void jpeg_decoder::locate_soi_marker()
1287
+ {
1288
+ uint lastchar, thischar;
1289
+ uint bytesleft;
1290
+
1291
+ lastchar = get_bits(8);
1292
+
1293
+ thischar = get_bits(8);
1294
+
1295
+ /* ok if it's a normal JPEG file without a special header */
1296
+
1297
+ if ((lastchar == 0xFF) && (thischar == M_SOI))
1298
+ return;
1299
+
1300
+ bytesleft = 4096; //512;
1301
+
1302
+ for ( ; ; )
1303
+ {
1304
+ if (--bytesleft == 0)
1305
+ stop_decoding(JPGD_NOT_JPEG);
1306
+
1307
+ lastchar = thischar;
1308
+
1309
+ thischar = get_bits(8);
1310
+
1311
+ if (lastchar == 0xFF)
1312
+ {
1313
+ if (thischar == M_SOI)
1314
+ break;
1315
+ else if (thischar == M_EOI) // get_bits will keep returning M_EOI if we read past the end
1316
+ stop_decoding(JPGD_NOT_JPEG);
1317
+ }
1318
+ }
1319
+
1320
+ // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
1321
+ thischar = (m_bit_buf >> 24) & 0xFF;
1322
+
1323
+ if (thischar != 0xFF)
1324
+ stop_decoding(JPGD_NOT_JPEG);
1325
+ }
1326
+
1327
+ // Find a start of frame (SOF) marker.
1328
+ void jpeg_decoder::locate_sof_marker()
1329
+ {
1330
+ locate_soi_marker();
1331
+
1332
+ int c = process_markers();
1333
+
1334
+ switch (c)
1335
+ {
1336
+ case M_SOF2:
1337
+ m_progressive_flag = JPGD_TRUE;
1338
+ case M_SOF0: /* baseline DCT */
1339
+ case M_SOF1: /* extended sequential DCT */
1340
+ {
1341
+ read_sof_marker();
1342
+ break;
1343
+ }
1344
+ case M_SOF9: /* Arithmitic coding */
1345
+ {
1346
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
1347
+ break;
1348
+ }
1349
+ default:
1350
+ {
1351
+ stop_decoding(JPGD_UNSUPPORTED_MARKER);
1352
+ break;
1353
+ }
1354
+ }
1355
+ }
1356
+
1357
+ // Find a start of scan (SOS) marker.
1358
+ int jpeg_decoder::locate_sos_marker()
1359
+ {
1360
+ int c;
1361
+
1362
+ c = process_markers();
1363
+
1364
+ if (c == M_EOI)
1365
+ return JPGD_FALSE;
1366
+ else if (c != M_SOS)
1367
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
1368
+
1369
+ read_sos_marker();
1370
+
1371
+ return JPGD_TRUE;
1372
+ }
1373
+
1374
+ // Reset everything to default/uninitialized state.
1375
+ void jpeg_decoder::init(jpeg_decoder_stream *pStream)
1376
+ {
1377
+ m_pMem_blocks = NULL;
1378
+ m_error_code = JPGD_SUCCESS;
1379
+ m_ready_flag = false;
1380
+ m_image_x_size = m_image_y_size = 0;
1381
+ m_pStream = pStream;
1382
+ m_progressive_flag = JPGD_FALSE;
1383
+
1384
+ memset(m_huff_ac, 0, sizeof(m_huff_ac));
1385
+ memset(m_huff_num, 0, sizeof(m_huff_num));
1386
+ memset(m_huff_val, 0, sizeof(m_huff_val));
1387
+ memset(m_quant, 0, sizeof(m_quant));
1388
+
1389
+ m_scan_type = 0;
1390
+ m_comps_in_frame = 0;
1391
+
1392
+ memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp));
1393
+ memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp));
1394
+ memset(m_comp_quant, 0, sizeof(m_comp_quant));
1395
+ memset(m_comp_ident, 0, sizeof(m_comp_ident));
1396
+ memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks));
1397
+ memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks));
1398
+
1399
+ m_comps_in_scan = 0;
1400
+ memset(m_comp_list, 0, sizeof(m_comp_list));
1401
+ memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab));
1402
+ memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab));
1403
+
1404
+ m_spectral_start = 0;
1405
+ m_spectral_end = 0;
1406
+ m_successive_low = 0;
1407
+ m_successive_high = 0;
1408
+ m_max_mcu_x_size = 0;
1409
+ m_max_mcu_y_size = 0;
1410
+ m_blocks_per_mcu = 0;
1411
+ m_max_blocks_per_row = 0;
1412
+ m_mcus_per_row = 0;
1413
+ m_mcus_per_col = 0;
1414
+ m_expanded_blocks_per_component = 0;
1415
+ m_expanded_blocks_per_mcu = 0;
1416
+ m_expanded_blocks_per_row = 0;
1417
+ m_freq_domain_chroma_upsample = false;
1418
+
1419
+ memset(m_mcu_org, 0, sizeof(m_mcu_org));
1420
+
1421
+ m_total_lines_left = 0;
1422
+ m_mcu_lines_left = 0;
1423
+ m_real_dest_bytes_per_scan_line = 0;
1424
+ m_dest_bytes_per_scan_line = 0;
1425
+ m_dest_bytes_per_pixel = 0;
1426
+
1427
+ memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs));
1428
+
1429
+ memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs));
1430
+ memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs));
1431
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
1432
+
1433
+ m_eob_run = 0;
1434
+
1435
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
1436
+
1437
+ m_pIn_buf_ofs = m_in_buf;
1438
+ m_in_buf_left = 0;
1439
+ m_eof_flag = false;
1440
+ m_tem_flag = 0;
1441
+
1442
+ memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start));
1443
+ memset(m_in_buf, 0, sizeof(m_in_buf));
1444
+ memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end));
1445
+
1446
+ m_restart_interval = 0;
1447
+ m_restarts_left = 0;
1448
+ m_next_restart_num = 0;
1449
+
1450
+ m_max_mcus_per_row = 0;
1451
+ m_max_blocks_per_mcu = 0;
1452
+ m_max_mcus_per_col = 0;
1453
+
1454
+ memset(m_last_dc_val, 0, sizeof(m_last_dc_val));
1455
+ m_pMCU_coefficients = NULL;
1456
+ m_pSample_buf = NULL;
1457
+
1458
+ m_total_bytes_read = 0;
1459
+
1460
+ m_pScan_line_0 = NULL;
1461
+ m_pScan_line_1 = NULL;
1462
+
1463
+ // Ready the input buffer.
1464
+ prep_in_buffer();
1465
+
1466
+ // Prime the bit buffer.
1467
+ m_bits_left = 16;
1468
+ m_bit_buf = 0;
1469
+
1470
+ get_bits(16);
1471
+ get_bits(16);
1472
+
1473
+ for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++)
1474
+ m_mcu_block_max_zag[i] = 64;
1475
+ }
1476
+
1477
+ #define SCALEBITS 16
1478
+ #define ONE_HALF ((int) 1 << (SCALEBITS-1))
1479
+ #define FIX(x) ((int) ((x) * (1L<<SCALEBITS) + 0.5f))
1480
+
1481
+ // Create a few tables that allow us to quickly convert YCbCr to RGB.
1482
+ void jpeg_decoder::create_look_ups()
1483
+ {
1484
+ for (int i = 0; i <= 255; i++)
1485
+ {
1486
+ int k = i - 128;
1487
+ m_crr[i] = ( FIX(1.40200f) * k + ONE_HALF) >> SCALEBITS;
1488
+ m_cbb[i] = ( FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS;
1489
+ m_crg[i] = (-FIX(0.71414f)) * k;
1490
+ m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF;
1491
+ }
1492
+ }
1493
+
1494
+ // This method throws back into the stream any bytes that where read
1495
+ // into the bit buffer during initial marker scanning.
1496
+ void jpeg_decoder::fix_in_buffer()
1497
+ {
1498
+ // In case any 0xFF's where pulled into the buffer during marker scanning.
1499
+ JPGD_ASSERT((m_bits_left & 7) == 0);
1500
+
1501
+ if (m_bits_left == 16)
1502
+ stuff_char( (uint8)(m_bit_buf & 0xFF));
1503
+
1504
+ if (m_bits_left >= 8)
1505
+ stuff_char( (uint8)((m_bit_buf >> 8) & 0xFF));
1506
+
1507
+ stuff_char((uint8)((m_bit_buf >> 16) & 0xFF));
1508
+ stuff_char((uint8)((m_bit_buf >> 24) & 0xFF));
1509
+
1510
+ m_bits_left = 16;
1511
+ get_bits_no_markers(16);
1512
+ get_bits_no_markers(16);
1513
+ }
1514
+
1515
+ void jpeg_decoder::transform_mcu(int mcu_row)
1516
+ {
1517
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
1518
+ uint8* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64;
1519
+
1520
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
1521
+ {
1522
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
1523
+ pSrc_ptr += 64;
1524
+ pDst_ptr += 64;
1525
+ }
1526
+ }
1527
+
1528
+ static const uint8 s_max_rc[64] =
1529
+ {
1530
+ 17, 18, 34, 50, 50, 51, 52, 52, 52, 68, 84, 84, 84, 84, 85, 86, 86, 86, 86, 86,
1531
+ 102, 118, 118, 118, 118, 118, 118, 119, 120, 120, 120, 120, 120, 120, 120, 136,
1532
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
1533
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136
1534
+ };
1535
+
1536
+ void jpeg_decoder::transform_mcu_expand(int mcu_row)
1537
+ {
1538
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
1539
+ uint8* pDst_ptr = m_pSample_buf + mcu_row * m_expanded_blocks_per_mcu * 64;
1540
+
1541
+ // Y IDCT
1542
+ int mcu_block;
1543
+ for (mcu_block = 0; mcu_block < m_expanded_blocks_per_component; mcu_block++)
1544
+ {
1545
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
1546
+ pSrc_ptr += 64;
1547
+ pDst_ptr += 64;
1548
+ }
1549
+
1550
+ // Chroma IDCT, with upsampling
1551
+ jpgd_block_t temp_block[64];
1552
+
1553
+ for (int i = 0; i < 2; i++)
1554
+ {
1555
+ DCT_Upsample::Matrix44 P, Q, R, S;
1556
+
1557
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1);
1558
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64);
1559
+
1560
+ switch (s_max_rc[m_mcu_block_max_zag[mcu_block++] - 1])
1561
+ {
1562
+ case 1*16+1:
1563
+ DCT_Upsample::P_Q<1, 1>::calc(P, Q, pSrc_ptr);
1564
+ DCT_Upsample::R_S<1, 1>::calc(R, S, pSrc_ptr);
1565
+ break;
1566
+ case 1*16+2:
1567
+ DCT_Upsample::P_Q<1, 2>::calc(P, Q, pSrc_ptr);
1568
+ DCT_Upsample::R_S<1, 2>::calc(R, S, pSrc_ptr);
1569
+ break;
1570
+ case 2*16+2:
1571
+ DCT_Upsample::P_Q<2, 2>::calc(P, Q, pSrc_ptr);
1572
+ DCT_Upsample::R_S<2, 2>::calc(R, S, pSrc_ptr);
1573
+ break;
1574
+ case 3*16+2:
1575
+ DCT_Upsample::P_Q<3, 2>::calc(P, Q, pSrc_ptr);
1576
+ DCT_Upsample::R_S<3, 2>::calc(R, S, pSrc_ptr);
1577
+ break;
1578
+ case 3*16+3:
1579
+ DCT_Upsample::P_Q<3, 3>::calc(P, Q, pSrc_ptr);
1580
+ DCT_Upsample::R_S<3, 3>::calc(R, S, pSrc_ptr);
1581
+ break;
1582
+ case 3*16+4:
1583
+ DCT_Upsample::P_Q<3, 4>::calc(P, Q, pSrc_ptr);
1584
+ DCT_Upsample::R_S<3, 4>::calc(R, S, pSrc_ptr);
1585
+ break;
1586
+ case 4*16+4:
1587
+ DCT_Upsample::P_Q<4, 4>::calc(P, Q, pSrc_ptr);
1588
+ DCT_Upsample::R_S<4, 4>::calc(R, S, pSrc_ptr);
1589
+ break;
1590
+ case 5*16+4:
1591
+ DCT_Upsample::P_Q<5, 4>::calc(P, Q, pSrc_ptr);
1592
+ DCT_Upsample::R_S<5, 4>::calc(R, S, pSrc_ptr);
1593
+ break;
1594
+ case 5*16+5:
1595
+ DCT_Upsample::P_Q<5, 5>::calc(P, Q, pSrc_ptr);
1596
+ DCT_Upsample::R_S<5, 5>::calc(R, S, pSrc_ptr);
1597
+ break;
1598
+ case 5*16+6:
1599
+ DCT_Upsample::P_Q<5, 6>::calc(P, Q, pSrc_ptr);
1600
+ DCT_Upsample::R_S<5, 6>::calc(R, S, pSrc_ptr);
1601
+ break;
1602
+ case 6*16+6:
1603
+ DCT_Upsample::P_Q<6, 6>::calc(P, Q, pSrc_ptr);
1604
+ DCT_Upsample::R_S<6, 6>::calc(R, S, pSrc_ptr);
1605
+ break;
1606
+ case 7*16+6:
1607
+ DCT_Upsample::P_Q<7, 6>::calc(P, Q, pSrc_ptr);
1608
+ DCT_Upsample::R_S<7, 6>::calc(R, S, pSrc_ptr);
1609
+ break;
1610
+ case 7*16+7:
1611
+ DCT_Upsample::P_Q<7, 7>::calc(P, Q, pSrc_ptr);
1612
+ DCT_Upsample::R_S<7, 7>::calc(R, S, pSrc_ptr);
1613
+ break;
1614
+ case 7*16+8:
1615
+ DCT_Upsample::P_Q<7, 8>::calc(P, Q, pSrc_ptr);
1616
+ DCT_Upsample::R_S<7, 8>::calc(R, S, pSrc_ptr);
1617
+ break;
1618
+ case 8*16+8:
1619
+ DCT_Upsample::P_Q<8, 8>::calc(P, Q, pSrc_ptr);
1620
+ DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
1621
+ break;
1622
+ default:
1623
+ JPGD_ASSERT(false);
1624
+ }
1625
+
1626
+ DCT_Upsample::Matrix44 a(P + Q); P -= Q;
1627
+ DCT_Upsample::Matrix44& b = P;
1628
+ DCT_Upsample::Matrix44 c(R + S); R -= S;
1629
+ DCT_Upsample::Matrix44& d = R;
1630
+
1631
+ DCT_Upsample::Matrix44::add_and_store(temp_block, a, c);
1632
+ idct_4x4(temp_block, pDst_ptr);
1633
+ pDst_ptr += 64;
1634
+
1635
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, a, c);
1636
+ idct_4x4(temp_block, pDst_ptr);
1637
+ pDst_ptr += 64;
1638
+
1639
+ DCT_Upsample::Matrix44::add_and_store(temp_block, b, d);
1640
+ idct_4x4(temp_block, pDst_ptr);
1641
+ pDst_ptr += 64;
1642
+
1643
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, b, d);
1644
+ idct_4x4(temp_block, pDst_ptr);
1645
+ pDst_ptr += 64;
1646
+
1647
+ pSrc_ptr += 64;
1648
+ }
1649
+ }
1650
+
1651
+ // Loads and dequantizes the next row of (already decoded) coefficients.
1652
+ // Progressive images only.
1653
+ void jpeg_decoder::load_next_row()
1654
+ {
1655
+ int i;
1656
+ jpgd_block_t *p;
1657
+ jpgd_quant_t *q;
1658
+ int mcu_row, mcu_block, row_block = 0;
1659
+ int component_num, component_id;
1660
+ int block_x_mcu[JPGD_MAX_COMPONENTS];
1661
+
1662
+ memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int));
1663
+
1664
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
1665
+ {
1666
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
1667
+
1668
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
1669
+ {
1670
+ component_id = m_mcu_org[mcu_block];
1671
+ q = m_quant[m_comp_quant[component_id]];
1672
+
1673
+ p = m_pMCU_coefficients + 64 * mcu_block;
1674
+
1675
+ jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
1676
+ jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
1677
+ p[0] = pDC[0];
1678
+ memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t));
1679
+
1680
+ for (i = 63; i > 0; i--)
1681
+ if (p[g_ZAG[i]])
1682
+ break;
1683
+
1684
+ m_mcu_block_max_zag[mcu_block] = i + 1;
1685
+
1686
+ for ( ; i >= 0; i--)
1687
+ if (p[g_ZAG[i]])
1688
+ p[g_ZAG[i]] = static_cast<jpgd_block_t>(p[g_ZAG[i]] * q[i]);
1689
+
1690
+ row_block++;
1691
+
1692
+ if (m_comps_in_scan == 1)
1693
+ block_x_mcu[component_id]++;
1694
+ else
1695
+ {
1696
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id])
1697
+ {
1698
+ block_x_mcu_ofs = 0;
1699
+
1700
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id])
1701
+ {
1702
+ block_y_mcu_ofs = 0;
1703
+
1704
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
1705
+ }
1706
+ }
1707
+ }
1708
+ }
1709
+
1710
+ if (m_freq_domain_chroma_upsample)
1711
+ transform_mcu_expand(mcu_row);
1712
+ else
1713
+ transform_mcu(mcu_row);
1714
+ }
1715
+
1716
+ if (m_comps_in_scan == 1)
1717
+ m_block_y_mcu[m_comp_list[0]]++;
1718
+ else
1719
+ {
1720
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++)
1721
+ {
1722
+ component_id = m_comp_list[component_num];
1723
+
1724
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
1725
+ }
1726
+ }
1727
+ }
1728
+
1729
+ // Restart interval processing.
1730
+ void jpeg_decoder::process_restart()
1731
+ {
1732
+ int i;
1733
+ int c = 0;
1734
+
1735
+ // Align to a byte boundry
1736
+ // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
1737
+ //get_bits_no_markers(m_bits_left & 7);
1738
+
1739
+ // Let's scan a little bit to find the marker, but not _too_ far.
1740
+ // 1536 is a "fudge factor" that determines how much to scan.
1741
+ for (i = 1536; i > 0; i--)
1742
+ if (get_char() == 0xFF)
1743
+ break;
1744
+
1745
+ if (i == 0)
1746
+ stop_decoding(JPGD_BAD_RESTART_MARKER);
1747
+
1748
+ for ( ; i > 0; i--)
1749
+ if ((c = get_char()) != 0xFF)
1750
+ break;
1751
+
1752
+ if (i == 0)
1753
+ stop_decoding(JPGD_BAD_RESTART_MARKER);
1754
+
1755
+ // Is it the expected marker? If not, something bad happened.
1756
+ if (c != (m_next_restart_num + M_RST0))
1757
+ stop_decoding(JPGD_BAD_RESTART_MARKER);
1758
+
1759
+ // Reset each component's DC prediction values.
1760
+ memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint));
1761
+
1762
+ m_eob_run = 0;
1763
+
1764
+ m_restarts_left = m_restart_interval;
1765
+
1766
+ m_next_restart_num = (m_next_restart_num + 1) & 7;
1767
+
1768
+ // Get the bit buffer going again...
1769
+
1770
+ m_bits_left = 16;
1771
+ get_bits_no_markers(16);
1772
+ get_bits_no_markers(16);
1773
+ }
1774
+
1775
+ static inline int dequantize_ac(int c, int q) { c *= q; return c; }
1776
+
1777
+ // Decodes and dequantizes the next row of coefficients.
1778
+ void jpeg_decoder::decode_next_row()
1779
+ {
1780
+ int row_block = 0;
1781
+
1782
+ for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
1783
+ {
1784
+ if ((m_restart_interval) && (m_restarts_left == 0))
1785
+ process_restart();
1786
+
1787
+ jpgd_block_t* p = m_pMCU_coefficients;
1788
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64)
1789
+ {
1790
+ int component_id = m_mcu_org[mcu_block];
1791
+ jpgd_quant_t* q = m_quant[m_comp_quant[component_id]];
1792
+
1793
+ int r, s;
1794
+ s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r);
1795
+ s = HUFF_EXTEND(r, s);
1796
+
1797
+ m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]);
1798
+
1799
+ p[0] = static_cast<jpgd_block_t>(s * q[0]);
1800
+
1801
+ int prev_num_set = m_mcu_block_max_zag[mcu_block];
1802
+
1803
+ huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]];
1804
+
1805
+ int k;
1806
+ for (k = 1; k < 64; k++)
1807
+ {
1808
+ int extra_bits;
1809
+ s = huff_decode(pH, extra_bits);
1810
+
1811
+ r = s >> 4;
1812
+ s &= 15;
1813
+
1814
+ if (s)
1815
+ {
1816
+ if (r)
1817
+ {
1818
+ if ((k + r) > 63)
1819
+ stop_decoding(JPGD_DECODE_ERROR);
1820
+
1821
+ if (k < prev_num_set)
1822
+ {
1823
+ int n = JPGD_MIN(r, prev_num_set - k);
1824
+ int kt = k;
1825
+ while (n--)
1826
+ p[g_ZAG[kt++]] = 0;
1827
+ }
1828
+
1829
+ k += r;
1830
+ }
1831
+
1832
+ s = HUFF_EXTEND(extra_bits, s);
1833
+
1834
+ JPGD_ASSERT(k < 64);
1835
+
1836
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
1837
+ }
1838
+ else
1839
+ {
1840
+ if (r == 15)
1841
+ {
1842
+ if ((k + 16) > 64)
1843
+ stop_decoding(JPGD_DECODE_ERROR);
1844
+
1845
+ if (k < prev_num_set)
1846
+ {
1847
+ int n = JPGD_MIN(16, prev_num_set - k);
1848
+ int kt = k;
1849
+ while (n--)
1850
+ {
1851
+ JPGD_ASSERT(kt <= 63);
1852
+ p[g_ZAG[kt++]] = 0;
1853
+ }
1854
+ }
1855
+
1856
+ k += 16 - 1; // - 1 because the loop counter is k
1857
+ // BEGIN EPIC MOD
1858
+ JPGD_ASSERT(k < 64 && p[g_ZAG[k]] == 0);
1859
+ // END EPIC MOD
1860
+ }
1861
+ else
1862
+ break;
1863
+ }
1864
+ }
1865
+
1866
+ if (k < prev_num_set)
1867
+ {
1868
+ int kt = k;
1869
+ while (kt < prev_num_set)
1870
+ p[g_ZAG[kt++]] = 0;
1871
+ }
1872
+
1873
+ m_mcu_block_max_zag[mcu_block] = k;
1874
+
1875
+ row_block++;
1876
+ }
1877
+
1878
+ if (m_freq_domain_chroma_upsample)
1879
+ transform_mcu_expand(mcu_row);
1880
+ else
1881
+ transform_mcu(mcu_row);
1882
+
1883
+ m_restarts_left--;
1884
+ }
1885
+ }
1886
+
1887
+ // YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB
1888
+ void jpeg_decoder::H1V1Convert()
1889
+ {
1890
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
1891
+ uint8 *d = m_pScan_line_0;
1892
+ uint8 *s = m_pSample_buf + row * 8;
1893
+
1894
+ for (int i = m_max_mcus_per_row; i > 0; i--)
1895
+ {
1896
+ for (int j = 0; j < 8; j++)
1897
+ {
1898
+ int y = s[j];
1899
+ int cb = s[64+j];
1900
+ int cr = s[128+j];
1901
+
1902
+ if (jpg_format == ERGBFormatJPG::BGRA)
1903
+ {
1904
+ d[0] = clamp(y + m_cbb[cb]);
1905
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
1906
+ d[2] = clamp(y + m_crr[cr]);
1907
+ d[3] = 255;
1908
+ }
1909
+ else
1910
+ {
1911
+ d[0] = clamp(y + m_crr[cr]);
1912
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
1913
+ d[2] = clamp(y + m_cbb[cb]);
1914
+ d[3] = 255;
1915
+ }
1916
+ d += 4;
1917
+ }
1918
+
1919
+ s += 64*3;
1920
+ }
1921
+ }
1922
+
1923
+ // YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB
1924
+ void jpeg_decoder::H2V1Convert()
1925
+ {
1926
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
1927
+ uint8 *d0 = m_pScan_line_0;
1928
+ uint8 *y = m_pSample_buf + row * 8;
1929
+ uint8 *c = m_pSample_buf + 2*64 + row * 8;
1930
+
1931
+ for (int i = m_max_mcus_per_row; i > 0; i--)
1932
+ {
1933
+ for (int l = 0; l < 2; l++)
1934
+ {
1935
+ for (int j = 0; j < 4; j++)
1936
+ {
1937
+ int cb = c[0];
1938
+ int cr = c[64];
1939
+
1940
+ int rc = m_crr[cr];
1941
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
1942
+ int bc = m_cbb[cb];
1943
+
1944
+ int yy = y[j<<1];
1945
+ if (jpg_format == ERGBFormatJPG::BGRA)
1946
+ {
1947
+ d0[0] = clamp(yy+bc);
1948
+ d0[1] = clamp(yy+gc);
1949
+ d0[2] = clamp(yy+rc);
1950
+ d0[3] = 255;
1951
+ yy = y[(j<<1)+1];
1952
+ d0[4] = clamp(yy+bc);
1953
+ d0[5] = clamp(yy+gc);
1954
+ d0[6] = clamp(yy+rc);
1955
+ d0[7] = 255;
1956
+ }
1957
+ else
1958
+ {
1959
+ d0[0] = clamp(yy+rc);
1960
+ d0[1] = clamp(yy+gc);
1961
+ d0[2] = clamp(yy+bc);
1962
+ d0[3] = 255;
1963
+ yy = y[(j<<1)+1];
1964
+ d0[4] = clamp(yy+rc);
1965
+ d0[5] = clamp(yy+gc);
1966
+ d0[6] = clamp(yy+bc);
1967
+ d0[7] = 255;
1968
+ }
1969
+
1970
+ d0 += 8;
1971
+
1972
+ c++;
1973
+ }
1974
+ y += 64;
1975
+ }
1976
+
1977
+ y += 64*4 - 64*2;
1978
+ c += 64*4 - 8;
1979
+ }
1980
+ }
1981
+
1982
+ // YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB
1983
+ void jpeg_decoder::H1V2Convert()
1984
+ {
1985
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
1986
+ uint8 *d0 = m_pScan_line_0;
1987
+ uint8 *d1 = m_pScan_line_1;
1988
+ uint8 *y;
1989
+ uint8 *c;
1990
+
1991
+ if (row < 8)
1992
+ y = m_pSample_buf + row * 8;
1993
+ else
1994
+ y = m_pSample_buf + 64*1 + (row & 7) * 8;
1995
+
1996
+ c = m_pSample_buf + 64*2 + (row >> 1) * 8;
1997
+
1998
+ for (int i = m_max_mcus_per_row; i > 0; i--)
1999
+ {
2000
+ for (int j = 0; j < 8; j++)
2001
+ {
2002
+ int cb = c[0+j];
2003
+ int cr = c[64+j];
2004
+
2005
+ int rc = m_crr[cr];
2006
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
2007
+ int bc = m_cbb[cb];
2008
+
2009
+ int yy = y[j];
2010
+ if (jpg_format == ERGBFormatJPG::BGRA)
2011
+ {
2012
+ d0[0] = clamp(yy+bc);
2013
+ d0[1] = clamp(yy+gc);
2014
+ d0[2] = clamp(yy+rc);
2015
+ d0[3] = 255;
2016
+ yy = y[8+j];
2017
+ d1[0] = clamp(yy+bc);
2018
+ d1[1] = clamp(yy+gc);
2019
+ d1[2] = clamp(yy+rc);
2020
+ d1[3] = 255;
2021
+ }
2022
+ else
2023
+ {
2024
+ d0[0] = clamp(yy+rc);
2025
+ d0[1] = clamp(yy+gc);
2026
+ d0[2] = clamp(yy+bc);
2027
+ d0[3] = 255;
2028
+ yy = y[8+j];
2029
+ d1[0] = clamp(yy+rc);
2030
+ d1[1] = clamp(yy+gc);
2031
+ d1[2] = clamp(yy+bc);
2032
+ d1[3] = 255;
2033
+ }
2034
+
2035
+ d0 += 4;
2036
+ d1 += 4;
2037
+ }
2038
+
2039
+ y += 64*4;
2040
+ c += 64*4;
2041
+ }
2042
+ }
2043
+
2044
+ // YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB
2045
+ void jpeg_decoder::H2V2Convert()
2046
+ {
2047
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
2048
+ uint8 *d0 = m_pScan_line_0;
2049
+ uint8 *d1 = m_pScan_line_1;
2050
+ uint8 *y;
2051
+ uint8 *c;
2052
+
2053
+ if (row < 8)
2054
+ y = m_pSample_buf + row * 8;
2055
+ else
2056
+ y = m_pSample_buf + 64*2 + (row & 7) * 8;
2057
+
2058
+ c = m_pSample_buf + 64*4 + (row >> 1) * 8;
2059
+
2060
+ for (int i = m_max_mcus_per_row; i > 0; i--)
2061
+ {
2062
+ for (int l = 0; l < 2; l++)
2063
+ {
2064
+ for (int j = 0; j < 8; j += 2)
2065
+ {
2066
+ int cb = c[0];
2067
+ int cr = c[64];
2068
+
2069
+ int rc = m_crr[cr];
2070
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
2071
+ int bc = m_cbb[cb];
2072
+
2073
+ int yy = y[j];
2074
+ if (jpg_format == ERGBFormatJPG::BGRA)
2075
+ {
2076
+ d0[0] = clamp(yy+bc);
2077
+ d0[1] = clamp(yy+gc);
2078
+ d0[2] = clamp(yy+rc);
2079
+ d0[3] = 255;
2080
+ yy = y[j+1];
2081
+ d0[4] = clamp(yy+bc);
2082
+ d0[5] = clamp(yy+gc);
2083
+ d0[6] = clamp(yy+rc);
2084
+ d0[7] = 255;
2085
+ yy = y[j+8];
2086
+ d1[0] = clamp(yy+bc);
2087
+ d1[1] = clamp(yy+gc);
2088
+ d1[2] = clamp(yy+rc);
2089
+ d1[3] = 255;
2090
+ yy = y[j+8+1];
2091
+ d1[4] = clamp(yy+bc);
2092
+ d1[5] = clamp(yy+gc);
2093
+ d1[6] = clamp(yy+rc);
2094
+ d1[7] = 255;
2095
+ }
2096
+ else
2097
+ {
2098
+ d0[0] = clamp(yy+rc);
2099
+ d0[1] = clamp(yy+gc);
2100
+ d0[2] = clamp(yy+bc);
2101
+ d0[3] = 255;
2102
+ yy = y[j+1];
2103
+ d0[4] = clamp(yy+rc);
2104
+ d0[5] = clamp(yy+gc);
2105
+ d0[6] = clamp(yy+bc);
2106
+ d0[7] = 255;
2107
+ yy = y[j+8];
2108
+ d1[0] = clamp(yy+rc);
2109
+ d1[1] = clamp(yy+gc);
2110
+ d1[2] = clamp(yy+bc);
2111
+ d1[3] = 255;
2112
+ yy = y[j+8+1];
2113
+ d1[4] = clamp(yy+rc);
2114
+ d1[5] = clamp(yy+gc);
2115
+ d1[6] = clamp(yy+bc);
2116
+ d1[7] = 255;
2117
+ }
2118
+
2119
+ d0 += 8;
2120
+ d1 += 8;
2121
+
2122
+ c++;
2123
+ }
2124
+ y += 64;
2125
+ }
2126
+
2127
+ y += 64*6 - 64*2;
2128
+ c += 64*6 - 8;
2129
+ }
2130
+ }
2131
+
2132
+ // Y (1 block per MCU) to 8-bit grayscale
2133
+ void jpeg_decoder::gray_convert()
2134
+ {
2135
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
2136
+ uint8 *d = m_pScan_line_0;
2137
+ uint8 *s = m_pSample_buf + row * 8;
2138
+
2139
+ for (int i = m_max_mcus_per_row; i > 0; i--)
2140
+ {
2141
+ *(uint *)d = *(uint *)s;
2142
+ *(uint *)(&d[4]) = *(uint *)(&s[4]);
2143
+
2144
+ s += 64;
2145
+ d += 8;
2146
+ }
2147
+ }
2148
+
2149
+ void jpeg_decoder::expanded_convert()
2150
+ {
2151
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
2152
+
2153
+ uint8* Py = m_pSample_buf + (row / 8) * 64 * m_comp_h_samp[0] + (row & 7) * 8;
2154
+
2155
+ uint8* d = m_pScan_line_0;
2156
+
2157
+ for (int i = m_max_mcus_per_row; i > 0; i--)
2158
+ {
2159
+ for (int k = 0; k < m_max_mcu_x_size; k += 8)
2160
+ {
2161
+ const int Y_ofs = k * 8;
2162
+ const int Cb_ofs = Y_ofs + 64 * m_expanded_blocks_per_component;
2163
+ const int Cr_ofs = Y_ofs + 64 * m_expanded_blocks_per_component * 2;
2164
+ for (int j = 0; j < 8; j++)
2165
+ {
2166
+ int y = Py[Y_ofs + j];
2167
+ int cb = Py[Cb_ofs + j];
2168
+ int cr = Py[Cr_ofs + j];
2169
+
2170
+ if (jpg_format == ERGBFormatJPG::BGRA)
2171
+ {
2172
+ d[0] = clamp(y + m_cbb[cb]);
2173
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
2174
+ d[2] = clamp(y + m_crr[cr]);
2175
+ d[3] = 255;
2176
+ }
2177
+ else
2178
+ {
2179
+ d[0] = clamp(y + m_crr[cr]);
2180
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
2181
+ d[2] = clamp(y + m_cbb[cb]);
2182
+ d[3] = 255;
2183
+ }
2184
+
2185
+ d += 4;
2186
+ }
2187
+ }
2188
+
2189
+ Py += 64 * m_expanded_blocks_per_mcu;
2190
+ }
2191
+ }
2192
+
2193
+ // Find end of image (EOI) marker, so we can return to the user the exact size of the input stream.
2194
+ void jpeg_decoder::find_eoi()
2195
+ {
2196
+ if (!m_progressive_flag)
2197
+ {
2198
+ // Attempt to read the EOI marker.
2199
+ //get_bits_no_markers(m_bits_left & 7);
2200
+
2201
+ // Prime the bit buffer
2202
+ m_bits_left = 16;
2203
+ get_bits(16);
2204
+ get_bits(16);
2205
+
2206
+ // The next marker _should_ be EOI
2207
+ process_markers();
2208
+ }
2209
+
2210
+ m_total_bytes_read -= m_in_buf_left;
2211
+ }
2212
+
2213
+ int jpeg_decoder::decode(const void** pScan_line, uint* pScan_line_len)
2214
+ {
2215
+ if ((m_error_code) || (!m_ready_flag))
2216
+ return JPGD_FAILED;
2217
+
2218
+ if (m_total_lines_left == 0)
2219
+ return JPGD_DONE;
2220
+
2221
+ if (m_mcu_lines_left == 0)
2222
+ {
2223
+ if (setjmp(m_jmp_state))
2224
+ return JPGD_FAILED;
2225
+
2226
+ if (m_progressive_flag)
2227
+ load_next_row();
2228
+ else
2229
+ decode_next_row();
2230
+
2231
+ // Find the EOI marker if that was the last row.
2232
+ if (m_total_lines_left <= m_max_mcu_y_size)
2233
+ find_eoi();
2234
+
2235
+ m_mcu_lines_left = m_max_mcu_y_size;
2236
+ }
2237
+
2238
+ if (m_freq_domain_chroma_upsample)
2239
+ {
2240
+ expanded_convert();
2241
+ *pScan_line = m_pScan_line_0;
2242
+ }
2243
+ else
2244
+ {
2245
+ switch (m_scan_type)
2246
+ {
2247
+ case JPGD_YH2V2:
2248
+ {
2249
+ if ((m_mcu_lines_left & 1) == 0)
2250
+ {
2251
+ H2V2Convert();
2252
+ *pScan_line = m_pScan_line_0;
2253
+ }
2254
+ else
2255
+ *pScan_line = m_pScan_line_1;
2256
+
2257
+ break;
2258
+ }
2259
+ case JPGD_YH2V1:
2260
+ {
2261
+ H2V1Convert();
2262
+ *pScan_line = m_pScan_line_0;
2263
+ break;
2264
+ }
2265
+ case JPGD_YH1V2:
2266
+ {
2267
+ if ((m_mcu_lines_left & 1) == 0)
2268
+ {
2269
+ H1V2Convert();
2270
+ *pScan_line = m_pScan_line_0;
2271
+ }
2272
+ else
2273
+ *pScan_line = m_pScan_line_1;
2274
+
2275
+ break;
2276
+ }
2277
+ case JPGD_YH1V1:
2278
+ {
2279
+ H1V1Convert();
2280
+ *pScan_line = m_pScan_line_0;
2281
+ break;
2282
+ }
2283
+ case JPGD_GRAYSCALE:
2284
+ {
2285
+ gray_convert();
2286
+ *pScan_line = m_pScan_line_0;
2287
+
2288
+ break;
2289
+ }
2290
+ }
2291
+ }
2292
+
2293
+ *pScan_line_len = m_real_dest_bytes_per_scan_line;
2294
+
2295
+ m_mcu_lines_left--;
2296
+ m_total_lines_left--;
2297
+
2298
+ return JPGD_SUCCESS;
2299
+ }
2300
+
2301
+ // Creates the tables needed for efficient Huffman decoding.
2302
+ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
2303
+ {
2304
+ int p, i, l, si;
2305
+ uint8 huffsize[257];
2306
+ uint huffcode[257];
2307
+ uint code;
2308
+ uint subtree;
2309
+ int code_size;
2310
+ int lastp;
2311
+ int nextfreeentry;
2312
+ int currententry;
2313
+
2314
+ pH->ac_table = m_huff_ac[index] != 0;
2315
+
2316
+ p = 0;
2317
+
2318
+ for (l = 1; l <= 16; l++)
2319
+ {
2320
+ for (i = 1; i <= m_huff_num[index][l]; i++)
2321
+ huffsize[p++] = static_cast<uint8>(l);
2322
+ }
2323
+
2324
+ huffsize[p] = 0;
2325
+
2326
+ lastp = p;
2327
+
2328
+ code = 0;
2329
+ si = huffsize[0];
2330
+ p = 0;
2331
+
2332
+ while (huffsize[p])
2333
+ {
2334
+ while (huffsize[p] == si)
2335
+ {
2336
+ huffcode[p++] = code;
2337
+ code++;
2338
+ }
2339
+
2340
+ code <<= 1;
2341
+ si++;
2342
+ }
2343
+
2344
+ memset(pH->look_up, 0, sizeof(pH->look_up));
2345
+ memset(pH->look_up2, 0, sizeof(pH->look_up2));
2346
+ memset(pH->tree, 0, sizeof(pH->tree));
2347
+ memset(pH->code_size, 0, sizeof(pH->code_size));
2348
+
2349
+ nextfreeentry = -1;
2350
+
2351
+ p = 0;
2352
+
2353
+ while (p < lastp)
2354
+ {
2355
+ i = m_huff_val[index][p];
2356
+ code = huffcode[p];
2357
+ code_size = huffsize[p];
2358
+
2359
+ pH->code_size[i] = static_cast<uint8>(code_size);
2360
+
2361
+ if (code_size <= 8)
2362
+ {
2363
+ code <<= (8 - code_size);
2364
+
2365
+ for (l = 1 << (8 - code_size); l > 0; l--)
2366
+ {
2367
+ JPGD_ASSERT(i < 256);
2368
+
2369
+ pH->look_up[code] = i;
2370
+
2371
+ bool has_extrabits = false;
2372
+ int extra_bits = 0;
2373
+ int num_extra_bits = i & 15;
2374
+
2375
+ int bits_to_fetch = code_size;
2376
+ if (num_extra_bits)
2377
+ {
2378
+ int total_codesize = code_size + num_extra_bits;
2379
+ if (total_codesize <= 8)
2380
+ {
2381
+ has_extrabits = true;
2382
+ extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize));
2383
+ JPGD_ASSERT(extra_bits <= 0x7FFF);
2384
+ bits_to_fetch += num_extra_bits;
2385
+ }
2386
+ }
2387
+
2388
+ if (!has_extrabits)
2389
+ pH->look_up2[code] = i | (bits_to_fetch << 8);
2390
+ else
2391
+ pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8);
2392
+
2393
+ code++;
2394
+ }
2395
+ }
2396
+ else
2397
+ {
2398
+ subtree = (code >> (code_size - 8)) & 0xFF;
2399
+
2400
+ currententry = pH->look_up[subtree];
2401
+
2402
+ if (currententry == 0)
2403
+ {
2404
+ pH->look_up[subtree] = currententry = nextfreeentry;
2405
+ pH->look_up2[subtree] = currententry = nextfreeentry;
2406
+
2407
+ nextfreeentry -= 2;
2408
+ }
2409
+
2410
+ code <<= (16 - (code_size - 8));
2411
+
2412
+ for (l = code_size; l > 9; l--)
2413
+ {
2414
+ if ((code & 0x8000) == 0)
2415
+ currententry--;
2416
+
2417
+ if (pH->tree[-currententry - 1] == 0)
2418
+ {
2419
+ pH->tree[-currententry - 1] = nextfreeentry;
2420
+
2421
+ currententry = nextfreeentry;
2422
+
2423
+ nextfreeentry -= 2;
2424
+ }
2425
+ else
2426
+ currententry = pH->tree[-currententry - 1];
2427
+
2428
+ code <<= 1;
2429
+ }
2430
+
2431
+ if ((code & 0x8000) == 0)
2432
+ currententry--;
2433
+
2434
+ pH->tree[-currententry - 1] = i;
2435
+ }
2436
+
2437
+ p++;
2438
+ }
2439
+ }
2440
+
2441
+ // Verifies the quantization tables needed for this scan are available.
2442
+ void jpeg_decoder::check_quant_tables()
2443
+ {
2444
+ for (int i = 0; i < m_comps_in_scan; i++)
2445
+ if (m_quant[m_comp_quant[m_comp_list[i]]] == NULL)
2446
+ stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
2447
+ }
2448
+
2449
+ // Verifies that all the Huffman tables needed for this scan are available.
2450
+ void jpeg_decoder::check_huff_tables()
2451
+ {
2452
+ for (int i = 0; i < m_comps_in_scan; i++)
2453
+ {
2454
+ if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == NULL))
2455
+ stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
2456
+
2457
+ if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == NULL))
2458
+ stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
2459
+ }
2460
+
2461
+ for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++)
2462
+ if (m_huff_num[i])
2463
+ {
2464
+ if (!m_pHuff_tabs[i])
2465
+ m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables));
2466
+
2467
+ make_huff_table(i, m_pHuff_tabs[i]);
2468
+ }
2469
+ }
2470
+
2471
+ // Determines the component order inside each MCU.
2472
+ // Also calcs how many MCU's are on each row, etc.
2473
+ void jpeg_decoder::calc_mcu_block_order()
2474
+ {
2475
+ int component_num, component_id;
2476
+ int max_h_samp = 0, max_v_samp = 0;
2477
+
2478
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++)
2479
+ {
2480
+ if (m_comp_h_samp[component_id] > max_h_samp)
2481
+ max_h_samp = m_comp_h_samp[component_id];
2482
+
2483
+ if (m_comp_v_samp[component_id] > max_v_samp)
2484
+ max_v_samp = m_comp_v_samp[component_id];
2485
+ }
2486
+
2487
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++)
2488
+ {
2489
+ m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8;
2490
+ m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8;
2491
+ }
2492
+
2493
+ if (m_comps_in_scan == 1)
2494
+ {
2495
+ m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]];
2496
+ m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]];
2497
+ }
2498
+ else
2499
+ {
2500
+ m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp;
2501
+ m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp;
2502
+ }
2503
+
2504
+ if (m_comps_in_scan == 1)
2505
+ {
2506
+ m_mcu_org[0] = m_comp_list[0];
2507
+
2508
+ m_blocks_per_mcu = 1;
2509
+ }
2510
+ else
2511
+ {
2512
+ m_blocks_per_mcu = 0;
2513
+
2514
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++)
2515
+ {
2516
+ int num_blocks;
2517
+
2518
+ component_id = m_comp_list[component_num];
2519
+
2520
+ num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id];
2521
+
2522
+ while (num_blocks--)
2523
+ m_mcu_org[m_blocks_per_mcu++] = component_id;
2524
+ }
2525
+ }
2526
+ }
2527
+
2528
+ // Starts a new scan.
2529
+ int jpeg_decoder::init_scan()
2530
+ {
2531
+ if (!locate_sos_marker())
2532
+ return JPGD_FALSE;
2533
+
2534
+ calc_mcu_block_order();
2535
+
2536
+ check_huff_tables();
2537
+
2538
+ check_quant_tables();
2539
+
2540
+ memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint));
2541
+
2542
+ m_eob_run = 0;
2543
+
2544
+ if (m_restart_interval)
2545
+ {
2546
+ m_restarts_left = m_restart_interval;
2547
+ m_next_restart_num = 0;
2548
+ }
2549
+
2550
+ fix_in_buffer();
2551
+
2552
+ return JPGD_TRUE;
2553
+ }
2554
+
2555
+ // Starts a frame. Determines if the number of components or sampling factors
2556
+ // are supported.
2557
+ void jpeg_decoder::init_frame()
2558
+ {
2559
+ int i;
2560
+
2561
+ if (m_comps_in_frame == 1)
2562
+ {
2563
+ if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1))
2564
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
2565
+
2566
+ m_scan_type = JPGD_GRAYSCALE;
2567
+ m_max_blocks_per_mcu = 1;
2568
+ m_max_mcu_x_size = 8;
2569
+ m_max_mcu_y_size = 8;
2570
+ }
2571
+ else if (m_comps_in_frame == 3)
2572
+ {
2573
+ if ( ((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) ||
2574
+ ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)) )
2575
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
2576
+
2577
+ if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
2578
+ {
2579
+ m_scan_type = JPGD_YH1V1;
2580
+
2581
+ m_max_blocks_per_mcu = 3;
2582
+ m_max_mcu_x_size = 8;
2583
+ m_max_mcu_y_size = 8;
2584
+ }
2585
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
2586
+ {
2587
+ m_scan_type = JPGD_YH2V1;
2588
+ m_max_blocks_per_mcu = 4;
2589
+ m_max_mcu_x_size = 16;
2590
+ m_max_mcu_y_size = 8;
2591
+ }
2592
+ else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2))
2593
+ {
2594
+ m_scan_type = JPGD_YH1V2;
2595
+ m_max_blocks_per_mcu = 4;
2596
+ m_max_mcu_x_size = 8;
2597
+ m_max_mcu_y_size = 16;
2598
+ }
2599
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
2600
+ {
2601
+ m_scan_type = JPGD_YH2V2;
2602
+ m_max_blocks_per_mcu = 6;
2603
+ m_max_mcu_x_size = 16;
2604
+ m_max_mcu_y_size = 16;
2605
+ }
2606
+ else
2607
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
2608
+ }
2609
+ else
2610
+ stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
2611
+
2612
+ m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
2613
+ m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
2614
+
2615
+ // These values are for the *destination* pixels: after conversion.
2616
+ if (m_scan_type == JPGD_GRAYSCALE)
2617
+ m_dest_bytes_per_pixel = 1;
2618
+ else
2619
+ m_dest_bytes_per_pixel = 4;
2620
+
2621
+ m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel;
2622
+
2623
+ m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel);
2624
+
2625
+ // Initialize two scan line buffers.
2626
+ m_pScan_line_0 = (uint8 *)alloc(m_dest_bytes_per_scan_line, true);
2627
+ if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2))
2628
+ m_pScan_line_1 = (uint8 *)alloc(m_dest_bytes_per_scan_line, true);
2629
+
2630
+ m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
2631
+
2632
+ // Should never happen
2633
+ if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW)
2634
+ stop_decoding(JPGD_ASSERTION_ERROR);
2635
+
2636
+ // Allocate the coefficient buffer, enough for one MCU
2637
+ m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
2638
+
2639
+ for (i = 0; i < m_max_blocks_per_mcu; i++)
2640
+ m_mcu_block_max_zag[i] = 64;
2641
+
2642
+ m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0];
2643
+ m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame;
2644
+ m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu;
2645
+ // Freq. domain chroma upsampling is only supported for H2V2 subsampling factor.
2646
+ // BEGIN EPIC MOD
2647
+ #if JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING
2648
+ m_freq_domain_chroma_upsample = (m_expanded_blocks_per_mcu == 4*3);
2649
+ #else
2650
+ m_freq_domain_chroma_upsample = 0;
2651
+ #endif
2652
+ // END EPIC MOD
2653
+
2654
+ if (m_freq_domain_chroma_upsample)
2655
+ m_pSample_buf = (uint8 *)alloc(m_expanded_blocks_per_row * 64);
2656
+ else
2657
+ m_pSample_buf = (uint8 *)alloc(m_max_blocks_per_row * 64);
2658
+
2659
+ m_total_lines_left = m_image_y_size;
2660
+
2661
+ m_mcu_lines_left = 0;
2662
+
2663
+ create_look_ups();
2664
+ }
2665
+
2666
+ // The coeff_buf series of methods originally stored the coefficients
2667
+ // into a "virtual" file which was located in EMS, XMS, or a disk file. A cache
2668
+ // was used to make this process more efficient. Now, we can store the entire
2669
+ // thing in RAM.
2670
+ jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y)
2671
+ {
2672
+ coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf));
2673
+
2674
+ cb->block_num_x = block_num_x;
2675
+ cb->block_num_y = block_num_y;
2676
+ cb->block_len_x = block_len_x;
2677
+ cb->block_len_y = block_len_y;
2678
+ cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t);
2679
+ cb->pData = (uint8 *)alloc(cb->block_size * block_num_x * block_num_y, true);
2680
+ return cb;
2681
+ }
2682
+
2683
+ inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y)
2684
+ {
2685
+ JPGD_ASSERT((block_x < cb->block_num_x) && (block_y < cb->block_num_y));
2686
+ return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x));
2687
+ }
2688
+
2689
+ // The following methods decode the various types of m_blocks encountered
2690
+ // in progressively encoded images.
2691
+ void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2692
+ {
2693
+ int s, r;
2694
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
2695
+
2696
+ if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0)
2697
+ {
2698
+ r = pD->get_bits_no_markers(s);
2699
+ s = HUFF_EXTEND(r, s);
2700
+ }
2701
+
2702
+ pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
2703
+
2704
+ p[0] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
2705
+ }
2706
+
2707
+ void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2708
+ {
2709
+ if (pD->get_bits_no_markers(1))
2710
+ {
2711
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
2712
+
2713
+ p[0] |= (1 << pD->m_successive_low);
2714
+ }
2715
+ }
2716
+
2717
+ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2718
+ {
2719
+ int k, s, r;
2720
+
2721
+ if (pD->m_eob_run)
2722
+ {
2723
+ pD->m_eob_run--;
2724
+ return;
2725
+ }
2726
+
2727
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
2728
+
2729
+ for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++)
2730
+ {
2731
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
2732
+
2733
+ r = s >> 4;
2734
+ s &= 15;
2735
+
2736
+ if (s)
2737
+ {
2738
+ if ((k += r) > 63)
2739
+ pD->stop_decoding(JPGD_DECODE_ERROR);
2740
+
2741
+ r = pD->get_bits_no_markers(s);
2742
+ s = HUFF_EXTEND(r, s);
2743
+
2744
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
2745
+ }
2746
+ else
2747
+ {
2748
+ if (r == 15)
2749
+ {
2750
+ if ((k += 15) > 63)
2751
+ pD->stop_decoding(JPGD_DECODE_ERROR);
2752
+ }
2753
+ else
2754
+ {
2755
+ pD->m_eob_run = 1 << r;
2756
+
2757
+ if (r)
2758
+ pD->m_eob_run += pD->get_bits_no_markers(r);
2759
+
2760
+ pD->m_eob_run--;
2761
+
2762
+ break;
2763
+ }
2764
+ }
2765
+ }
2766
+ }
2767
+
2768
+ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2769
+ {
2770
+ int s, k, r;
2771
+ int p1 = 1 << pD->m_successive_low;
2772
+ int m1 = (-1) << pD->m_successive_low;
2773
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
2774
+
2775
+ k = pD->m_spectral_start;
2776
+
2777
+ if (pD->m_eob_run == 0)
2778
+ {
2779
+ for ( ; k <= pD->m_spectral_end; k++)
2780
+ {
2781
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
2782
+
2783
+ r = s >> 4;
2784
+ s &= 15;
2785
+
2786
+ if (s)
2787
+ {
2788
+ if (s != 1)
2789
+ pD->stop_decoding(JPGD_DECODE_ERROR);
2790
+
2791
+ if (pD->get_bits_no_markers(1))
2792
+ s = p1;
2793
+ else
2794
+ s = m1;
2795
+ }
2796
+ else
2797
+ {
2798
+ if (r != 15)
2799
+ {
2800
+ pD->m_eob_run = 1 << r;
2801
+
2802
+ if (r)
2803
+ pD->m_eob_run += pD->get_bits_no_markers(r);
2804
+
2805
+ break;
2806
+ }
2807
+ }
2808
+
2809
+ do
2810
+ {
2811
+ // BEGIN EPIC MOD
2812
+ JPGD_ASSERT(k < 64);
2813
+ // END EPIC MOD
2814
+
2815
+ jpgd_block_t *this_coef = p + g_ZAG[k];
2816
+
2817
+ if (*this_coef != 0)
2818
+ {
2819
+ if (pD->get_bits_no_markers(1))
2820
+ {
2821
+ if ((*this_coef & p1) == 0)
2822
+ {
2823
+ if (*this_coef >= 0)
2824
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
2825
+ else
2826
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
2827
+ }
2828
+ }
2829
+ }
2830
+ else
2831
+ {
2832
+ if (--r < 0)
2833
+ break;
2834
+ }
2835
+
2836
+ k++;
2837
+
2838
+ } while (k <= pD->m_spectral_end);
2839
+
2840
+ if ((s) && (k < 64))
2841
+ {
2842
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(s);
2843
+ }
2844
+ }
2845
+ }
2846
+
2847
+ if (pD->m_eob_run > 0)
2848
+ {
2849
+ for ( ; k <= pD->m_spectral_end; k++)
2850
+ {
2851
+ // BEGIN EPIC MOD
2852
+ JPGD_ASSERT(k < 64);
2853
+ // END EPIC MOD
2854
+
2855
+ jpgd_block_t *this_coef = p + g_ZAG[k];
2856
+
2857
+ if (*this_coef != 0)
2858
+ {
2859
+ if (pD->get_bits_no_markers(1))
2860
+ {
2861
+ if ((*this_coef & p1) == 0)
2862
+ {
2863
+ if (*this_coef >= 0)
2864
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
2865
+ else
2866
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
2867
+ }
2868
+ }
2869
+ }
2870
+ }
2871
+
2872
+ pD->m_eob_run--;
2873
+ }
2874
+ }
2875
+
2876
+ // Decode a scan in a progressively encoded image.
2877
+ void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
2878
+ {
2879
+ int mcu_row, mcu_col, mcu_block;
2880
+ int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS];
2881
+
2882
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
2883
+
2884
+ for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++)
2885
+ {
2886
+ int component_num, component_id;
2887
+
2888
+ memset(block_x_mcu, 0, sizeof(block_x_mcu));
2889
+
2890
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
2891
+ {
2892
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
2893
+
2894
+ if ((m_restart_interval) && (m_restarts_left == 0))
2895
+ process_restart();
2896
+
2897
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
2898
+ {
2899
+ component_id = m_mcu_org[mcu_block];
2900
+
2901
+ decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
2902
+
2903
+ if (m_comps_in_scan == 1)
2904
+ block_x_mcu[component_id]++;
2905
+ else
2906
+ {
2907
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id])
2908
+ {
2909
+ block_x_mcu_ofs = 0;
2910
+
2911
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id])
2912
+ {
2913
+ block_y_mcu_ofs = 0;
2914
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
2915
+ }
2916
+ }
2917
+ }
2918
+ }
2919
+
2920
+ m_restarts_left--;
2921
+ }
2922
+
2923
+ if (m_comps_in_scan == 1)
2924
+ m_block_y_mcu[m_comp_list[0]]++;
2925
+ else
2926
+ {
2927
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++)
2928
+ {
2929
+ component_id = m_comp_list[component_num];
2930
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
2931
+ }
2932
+ }
2933
+ }
2934
+ }
2935
+
2936
+ // Decode a progressively encoded image.
2937
+ void jpeg_decoder::init_progressive()
2938
+ {
2939
+ int i;
2940
+
2941
+ if (m_comps_in_frame == 4)
2942
+ stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
2943
+
2944
+ // Allocate the coefficient buffers.
2945
+ for (i = 0; i < m_comps_in_frame; i++)
2946
+ {
2947
+ m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1);
2948
+ m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8);
2949
+ }
2950
+
2951
+ for ( ; ; )
2952
+ {
2953
+ int dc_only_scan, refinement_scan;
2954
+ pDecode_block_func decode_block_func;
2955
+
2956
+ if (!init_scan())
2957
+ break;
2958
+
2959
+ dc_only_scan = (m_spectral_start == 0);
2960
+ refinement_scan = (m_successive_high != 0);
2961
+
2962
+ if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63))
2963
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
2964
+
2965
+ if (dc_only_scan)
2966
+ {
2967
+ if (m_spectral_end)
2968
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
2969
+ }
2970
+ else if (m_comps_in_scan != 1) /* AC scans can only contain one component */
2971
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
2972
+
2973
+ if ((refinement_scan) && (m_successive_low != m_successive_high - 1))
2974
+ stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
2975
+
2976
+ if (dc_only_scan)
2977
+ {
2978
+ if (refinement_scan)
2979
+ decode_block_func = decode_block_dc_refine;
2980
+ else
2981
+ decode_block_func = decode_block_dc_first;
2982
+ }
2983
+ else
2984
+ {
2985
+ if (refinement_scan)
2986
+ decode_block_func = decode_block_ac_refine;
2987
+ else
2988
+ decode_block_func = decode_block_ac_first;
2989
+ }
2990
+
2991
+ decode_scan(decode_block_func);
2992
+
2993
+ m_bits_left = 16;
2994
+ get_bits(16);
2995
+ get_bits(16);
2996
+ }
2997
+
2998
+ m_comps_in_scan = m_comps_in_frame;
2999
+
3000
+ for (i = 0; i < m_comps_in_frame; i++)
3001
+ m_comp_list[i] = i;
3002
+
3003
+ calc_mcu_block_order();
3004
+ }
3005
+
3006
+ void jpeg_decoder::init_sequential()
3007
+ {
3008
+ if (!init_scan())
3009
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
3010
+ }
3011
+
3012
+ void jpeg_decoder::decode_start()
3013
+ {
3014
+ init_frame();
3015
+
3016
+ if (m_progressive_flag)
3017
+ init_progressive();
3018
+ else
3019
+ init_sequential();
3020
+ }
3021
+
3022
+ void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
3023
+ {
3024
+ init(pStream);
3025
+ locate_sof_marker();
3026
+ }
3027
+
3028
+ jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
3029
+ {
3030
+ if (setjmp(m_jmp_state))
3031
+ return;
3032
+ decode_init(pStream);
3033
+ }
3034
+
3035
+ int jpeg_decoder::begin_decoding()
3036
+ {
3037
+ if (m_ready_flag)
3038
+ return JPGD_SUCCESS;
3039
+
3040
+ if (m_error_code)
3041
+ return JPGD_FAILED;
3042
+
3043
+ if (setjmp(m_jmp_state))
3044
+ return JPGD_FAILED;
3045
+
3046
+ decode_start();
3047
+
3048
+ m_ready_flag = true;
3049
+
3050
+ return JPGD_SUCCESS;
3051
+ }
3052
+
3053
+ jpeg_decoder::~jpeg_decoder()
3054
+ {
3055
+ free_all_blocks();
3056
+ }
3057
+
3058
+ jpeg_decoder_file_stream::jpeg_decoder_file_stream()
3059
+ {
3060
+ m_pFile = NULL;
3061
+ m_eof_flag = false;
3062
+ m_error_flag = false;
3063
+ }
3064
+
3065
+ void jpeg_decoder_file_stream::close()
3066
+ {
3067
+ if (m_pFile)
3068
+ {
3069
+ fclose(m_pFile);
3070
+ m_pFile = NULL;
3071
+ }
3072
+
3073
+ m_eof_flag = false;
3074
+ m_error_flag = false;
3075
+ }
3076
+
3077
+ jpeg_decoder_file_stream::~jpeg_decoder_file_stream()
3078
+ {
3079
+ close();
3080
+ }
3081
+
3082
+ bool jpeg_decoder_file_stream::open(const char *Pfilename)
3083
+ {
3084
+ close();
3085
+
3086
+ m_eof_flag = false;
3087
+ m_error_flag = false;
3088
+
3089
+ #if defined(_MSC_VER)
3090
+ m_pFile = NULL;
3091
+ fopen_s(&m_pFile, Pfilename, "rb");
3092
+ #else
3093
+ m_pFile = fopen(Pfilename, "rb");
3094
+ #endif
3095
+ return m_pFile != NULL;
3096
+ }
3097
+
3098
+ int jpeg_decoder_file_stream::read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag)
3099
+ {
3100
+ if (!m_pFile)
3101
+ return -1;
3102
+
3103
+ if (m_eof_flag)
3104
+ {
3105
+ *pEOF_flag = true;
3106
+ return 0;
3107
+ }
3108
+
3109
+ if (m_error_flag)
3110
+ return -1;
3111
+
3112
+ int bytes_read = static_cast<int>(fread(pBuf, 1, max_bytes_to_read, m_pFile));
3113
+ if (bytes_read < max_bytes_to_read)
3114
+ {
3115
+ if (ferror(m_pFile))
3116
+ {
3117
+ m_error_flag = true;
3118
+ return -1;
3119
+ }
3120
+
3121
+ m_eof_flag = true;
3122
+ *pEOF_flag = true;
3123
+ }
3124
+
3125
+ return bytes_read;
3126
+ }
3127
+
3128
+ bool jpeg_decoder_mem_stream::open(const uint8 *pSrc_data, uint size)
3129
+ {
3130
+ close();
3131
+ m_pSrc_data = pSrc_data;
3132
+ m_ofs = 0;
3133
+ m_size = size;
3134
+ return true;
3135
+ }
3136
+
3137
+ int jpeg_decoder_mem_stream::read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag)
3138
+ {
3139
+ *pEOF_flag = false;
3140
+
3141
+ if (!m_pSrc_data)
3142
+ return -1;
3143
+
3144
+ uint bytes_remaining = m_size - m_ofs;
3145
+ if ((uint)max_bytes_to_read > bytes_remaining)
3146
+ {
3147
+ max_bytes_to_read = bytes_remaining;
3148
+ *pEOF_flag = true;
3149
+ }
3150
+
3151
+ memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read);
3152
+ m_ofs += max_bytes_to_read;
3153
+
3154
+ return max_bytes_to_read;
3155
+ }
3156
+
3157
+ unsigned char *decompress_jpeg_image_from_stream(jpeg_decoder_stream *pStream, int *width, int *height, int *actual_comps, int req_comps)
3158
+ {
3159
+ if (!actual_comps)
3160
+ return NULL;
3161
+ *actual_comps = 0;
3162
+
3163
+ if ((!pStream) || (!width) || (!height) || (!req_comps))
3164
+ return NULL;
3165
+
3166
+ if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4))
3167
+ return NULL;
3168
+
3169
+ jpeg_decoder decoder(pStream);
3170
+ if (decoder.get_error_code() != JPGD_SUCCESS)
3171
+ return NULL;
3172
+
3173
+ const int image_width = decoder.get_width(), image_height = decoder.get_height();
3174
+ *width = image_width;
3175
+ *height = image_height;
3176
+ *actual_comps = decoder.get_num_components();
3177
+
3178
+ if (decoder.begin_decoding() != JPGD_SUCCESS)
3179
+ return NULL;
3180
+
3181
+ const int dst_bpl = image_width * req_comps;
3182
+
3183
+ uint8 *pImage_data = (uint8*)jpgd_malloc(dst_bpl * image_height);
3184
+ if (!pImage_data)
3185
+ return NULL;
3186
+
3187
+ for (int y = 0; y < image_height; y++)
3188
+ {
3189
+ const uint8* pScan_line = 0;
3190
+ uint scan_line_len;
3191
+ if (decoder.decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS)
3192
+ {
3193
+ jpgd_free(pImage_data);
3194
+ return NULL;
3195
+ }
3196
+
3197
+ uint8 *pDst = pImage_data + y * dst_bpl;
3198
+
3199
+ if (((req_comps == 4) && (decoder.get_num_components() == 3)) ||
3200
+ ((req_comps == 1) && (decoder.get_num_components() == 1)))
3201
+ {
3202
+ memcpy(pDst, pScan_line, dst_bpl);
3203
+ }
3204
+ else if (decoder.get_num_components() == 1)
3205
+ {
3206
+ if (req_comps == 3)
3207
+ {
3208
+ for (int x = 0; x < image_width; x++)
3209
+ {
3210
+ uint8 luma = pScan_line[x];
3211
+ pDst[0] = luma;
3212
+ pDst[1] = luma;
3213
+ pDst[2] = luma;
3214
+ pDst += 3;
3215
+ }
3216
+ }
3217
+ else
3218
+ {
3219
+ for (int x = 0; x < image_width; x++)
3220
+ {
3221
+ uint8 luma = pScan_line[x];
3222
+ pDst[0] = luma;
3223
+ pDst[1] = luma;
3224
+ pDst[2] = luma;
3225
+ pDst[3] = 255;
3226
+ pDst += 4;
3227
+ }
3228
+ }
3229
+ }
3230
+ else if (decoder.get_num_components() == 3)
3231
+ {
3232
+ if (req_comps == 1)
3233
+ {
3234
+ const int YR = 19595, YG = 38470, YB = 7471;
3235
+ for (int x = 0; x < image_width; x++)
3236
+ {
3237
+ int r = pScan_line[x*4+0];
3238
+ int g = pScan_line[x*4+1];
3239
+ int b = pScan_line[x*4+2];
3240
+ *pDst++ = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
3241
+ }
3242
+ }
3243
+ else
3244
+ {
3245
+ for (int x = 0; x < image_width; x++)
3246
+ {
3247
+ pDst[0] = pScan_line[x*4+0];
3248
+ pDst[1] = pScan_line[x*4+1];
3249
+ pDst[2] = pScan_line[x*4+2];
3250
+ pDst += 3;
3251
+ }
3252
+ }
3253
+ }
3254
+ }
3255
+
3256
+ return pImage_data;
3257
+ }
3258
+
3259
+ // BEGIN EPIC MOD
3260
+ unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps, int format)
3261
+ {
3262
+ jpg_format = (ERGBFormatJPG)format;
3263
+ // EMD EPIC MOD
3264
+ jpgd::jpeg_decoder_mem_stream mem_stream(pSrc_data, src_data_size);
3265
+ return decompress_jpeg_image_from_stream(&mem_stream, width, height, actual_comps, req_comps);
3266
+ }
3267
+
3268
+ unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *width, int *height, int *actual_comps, int req_comps)
3269
+ {
3270
+ jpgd::jpeg_decoder_file_stream file_stream;
3271
+ if (!file_stream.open(pSrc_filename))
3272
+ return NULL;
3273
+ return decompress_jpeg_image_from_stream(&file_stream, width, height, actual_comps, req_comps);
3274
+ }
3275
+
3276
+ } // namespace jpgd
crazy_functions/test_project/cpp/libJPG/jpgd.h ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // jpgd.h - C++ class for JPEG decompression.
2
+ // Public domain, Rich Geldreich <richgel99@gmail.com>
3
+ #ifndef JPEG_DECODER_H
4
+ #define JPEG_DECODER_H
5
+
6
+ #include <stdlib.h>
7
+ #include <stdio.h>
8
+ #include <setjmp.h>
9
+
10
+ namespace jpgd
11
+ {
12
+ typedef unsigned char uint8;
13
+ typedef signed short int16;
14
+ typedef unsigned short uint16;
15
+ typedef unsigned int uint;
16
+ typedef signed int int32;
17
+
18
+ // Loads a JPEG image from a memory buffer or a file.
19
+ // req_comps can be 1 (grayscale), 3 (RGB), or 4 (RGBA).
20
+ // On return, width/height will be set to the image's dimensions, and actual_comps will be set to the either 1 (grayscale) or 3 (RGB).
21
+ // Notes: For more control over where and how the source data is read, see the decompress_jpeg_image_from_stream() function below, or call the jpeg_decoder class directly.
22
+ // Requesting a 8 or 32bpp image is currently a little faster than 24bpp because the jpeg_decoder class itself currently always unpacks to either 8 or 32bpp.
23
+ // BEGIN EPIC MOD
24
+ //unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps);
25
+ unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps, int format);
26
+ // END EPIC MOD
27
+ unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *width, int *height, int *actual_comps, int req_comps);
28
+
29
+ // Success/failure error codes.
30
+ enum jpgd_status
31
+ {
32
+ JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
33
+ JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
34
+ JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
35
+ JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
36
+ JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
37
+ JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
38
+ JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
39
+ JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
40
+ JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM
41
+ };
42
+
43
+ // Input stream interface.
44
+ // Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
45
+ // The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
46
+ // It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
47
+ // Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
48
+ class jpeg_decoder_stream
49
+ {
50
+ public:
51
+ jpeg_decoder_stream() { }
52
+ virtual ~jpeg_decoder_stream() { }
53
+
54
+ // The read() method is called when the internal input buffer is empty.
55
+ // Parameters:
56
+ // pBuf - input buffer
57
+ // max_bytes_to_read - maximum bytes that can be written to pBuf
58
+ // pEOF_flag - set this to true if at end of stream (no more bytes remaining)
59
+ // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
60
+ // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
61
+ virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0;
62
+ };
63
+
64
+ // stdio FILE stream class.
65
+ class jpeg_decoder_file_stream : public jpeg_decoder_stream
66
+ {
67
+ jpeg_decoder_file_stream(const jpeg_decoder_file_stream &);
68
+ jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &);
69
+
70
+ FILE *m_pFile;
71
+ bool m_eof_flag, m_error_flag;
72
+
73
+ public:
74
+ jpeg_decoder_file_stream();
75
+ virtual ~jpeg_decoder_file_stream();
76
+
77
+ bool open(const char *Pfilename);
78
+ void close();
79
+
80
+ virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag);
81
+ };
82
+
83
+ // Memory stream class.
84
+ class jpeg_decoder_mem_stream : public jpeg_decoder_stream
85
+ {
86
+ const uint8 *m_pSrc_data;
87
+ uint m_ofs, m_size;
88
+
89
+ public:
90
+ jpeg_decoder_mem_stream() : m_pSrc_data(NULL), m_ofs(0), m_size(0) { }
91
+ jpeg_decoder_mem_stream(const uint8 *pSrc_data, uint size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) { }
92
+
93
+ virtual ~jpeg_decoder_mem_stream() { }
94
+
95
+ bool open(const uint8 *pSrc_data, uint size);
96
+ void close() { m_pSrc_data = NULL; m_ofs = 0; m_size = 0; }
97
+
98
+ virtual int read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag);
99
+ };
100
+
101
+ // Loads JPEG file from a jpeg_decoder_stream.
102
+ unsigned char *decompress_jpeg_image_from_stream(jpeg_decoder_stream *pStream, int *width, int *height, int *actual_comps, int req_comps);
103
+
104
+ enum
105
+ {
106
+ JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
107
+ JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
108
+ };
109
+
110
+ typedef int16 jpgd_quant_t;
111
+ typedef int16 jpgd_block_t;
112
+
113
+ class jpeg_decoder
114
+ {
115
+ public:
116
+ // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
117
+ // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
118
+ jpeg_decoder(jpeg_decoder_stream *pStream);
119
+
120
+ ~jpeg_decoder();
121
+
122
+ // Call this method after constructing the object to begin decompression.
123
+ // If JPGD_SUCCESS is returned you may then call decode() on each scanline.
124
+ int begin_decoding();
125
+
126
+ // Returns the next scan line.
127
+ // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
128
+ // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
129
+ // Returns JPGD_SUCCESS if a scan line has been returned.
130
+ // Returns JPGD_DONE if all scan lines have been returned.
131
+ // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
132
+ int decode(const void** pScan_line, uint* pScan_line_len);
133
+
134
+ inline jpgd_status get_error_code() const { return m_error_code; }
135
+
136
+ inline int get_width() const { return m_image_x_size; }
137
+ inline int get_height() const { return m_image_y_size; }
138
+
139
+ inline int get_num_components() const { return m_comps_in_frame; }
140
+
141
+ inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
142
+ inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
143
+
144
+ // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
145
+ inline int get_total_bytes_read() const { return m_total_bytes_read; }
146
+
147
+ private:
148
+ jpeg_decoder(const jpeg_decoder &);
149
+ jpeg_decoder &operator =(const jpeg_decoder &);
150
+
151
+ typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
152
+
153
+ struct huff_tables
154
+ {
155
+ bool ac_table;
156
+ uint look_up[256];
157
+ uint look_up2[256];
158
+ uint8 code_size[256];
159
+ uint tree[512];
160
+ };
161
+
162
+ struct coeff_buf
163
+ {
164
+ uint8 *pData;
165
+ int block_num_x, block_num_y;
166
+ int block_len_x, block_len_y;
167
+ int block_size;
168
+ };
169
+
170
+ struct mem_block
171
+ {
172
+ mem_block *m_pNext;
173
+ size_t m_used_count;
174
+ size_t m_size;
175
+ char m_data[1];
176
+ };
177
+
178
+ jmp_buf m_jmp_state;
179
+ mem_block *m_pMem_blocks;
180
+ int m_image_x_size;
181
+ int m_image_y_size;
182
+ jpeg_decoder_stream *m_pStream;
183
+ int m_progressive_flag;
184
+ uint8 m_huff_ac[JPGD_MAX_HUFF_TABLES];
185
+ uint8* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size
186
+ uint8* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size
187
+ jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
188
+ int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
189
+ int m_comps_in_frame; // # of components in frame
190
+ int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor
191
+ int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor
192
+ int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector
193
+ int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID
194
+ int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
195
+ int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
196
+ int m_comps_in_scan; // # of components in scan
197
+ int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan
198
+ int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector
199
+ int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector
200
+ int m_spectral_start; // spectral selection start
201
+ int m_spectral_end; // spectral selection end
202
+ int m_successive_low; // successive approximation low
203
+ int m_successive_high; // successive approximation high
204
+ int m_max_mcu_x_size; // MCU's max. X size in pixels
205
+ int m_max_mcu_y_size; // MCU's max. Y size in pixels
206
+ int m_blocks_per_mcu;
207
+ int m_max_blocks_per_row;
208
+ int m_mcus_per_row, m_mcus_per_col;
209
+ int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
210
+ int m_total_lines_left; // total # lines left in image
211
+ int m_mcu_lines_left; // total # lines left in this MCU
212
+ int m_real_dest_bytes_per_scan_line;
213
+ int m_dest_bytes_per_scan_line; // rounded up
214
+ int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
215
+ huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
216
+ coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
217
+ coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
218
+ int m_eob_run;
219
+ int m_block_y_mcu[JPGD_MAX_COMPONENTS];
220
+ uint8* m_pIn_buf_ofs;
221
+ int m_in_buf_left;
222
+ int m_tem_flag;
223
+ bool m_eof_flag;
224
+ uint8 m_in_buf_pad_start[128];
225
+ uint8 m_in_buf[JPGD_IN_BUF_SIZE + 128];
226
+ uint8 m_in_buf_pad_end[128];
227
+ int m_bits_left;
228
+ uint m_bit_buf;
229
+ int m_restart_interval;
230
+ int m_restarts_left;
231
+ int m_next_restart_num;
232
+ int m_max_mcus_per_row;
233
+ int m_max_blocks_per_mcu;
234
+ int m_expanded_blocks_per_mcu;
235
+ int m_expanded_blocks_per_row;
236
+ int m_expanded_blocks_per_component;
237
+ bool m_freq_domain_chroma_upsample;
238
+ int m_max_mcus_per_col;
239
+ uint m_last_dc_val[JPGD_MAX_COMPONENTS];
240
+ jpgd_block_t* m_pMCU_coefficients;
241
+ int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
242
+ uint8* m_pSample_buf;
243
+ int m_crr[256];
244
+ int m_cbb[256];
245
+ int m_crg[256];
246
+ int m_cbg[256];
247
+ uint8* m_pScan_line_0;
248
+ uint8* m_pScan_line_1;
249
+ jpgd_status m_error_code;
250
+ bool m_ready_flag;
251
+ int m_total_bytes_read;
252
+
253
+ void free_all_blocks();
254
+ // BEGIN EPIC MOD
255
+ UE_NORETURN void stop_decoding(jpgd_status status);
256
+ // END EPIC MOD
257
+ void *alloc(size_t n, bool zero = false);
258
+ void word_clear(void *p, uint16 c, uint n);
259
+ void prep_in_buffer();
260
+ void read_dht_marker();
261
+ void read_dqt_marker();
262
+ void read_sof_marker();
263
+ void skip_variable_marker();
264
+ void read_dri_marker();
265
+ void read_sos_marker();
266
+ int next_marker();
267
+ int process_markers();
268
+ void locate_soi_marker();
269
+ void locate_sof_marker();
270
+ int locate_sos_marker();
271
+ void init(jpeg_decoder_stream * pStream);
272
+ void create_look_ups();
273
+ void fix_in_buffer();
274
+ void transform_mcu(int mcu_row);
275
+ void transform_mcu_expand(int mcu_row);
276
+ coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
277
+ inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
278
+ void load_next_row();
279
+ void decode_next_row();
280
+ void make_huff_table(int index, huff_tables *pH);
281
+ void check_quant_tables();
282
+ void check_huff_tables();
283
+ void calc_mcu_block_order();
284
+ int init_scan();
285
+ void init_frame();
286
+ void process_restart();
287
+ void decode_scan(pDecode_block_func decode_block_func);
288
+ void init_progressive();
289
+ void init_sequential();
290
+ void decode_start();
291
+ void decode_init(jpeg_decoder_stream * pStream);
292
+ void H2V2Convert();
293
+ void H2V1Convert();
294
+ void H1V2Convert();
295
+ void H1V1Convert();
296
+ void gray_convert();
297
+ void expanded_convert();
298
+ void find_eoi();
299
+ inline uint get_char();
300
+ inline uint get_char(bool *pPadding_flag);
301
+ inline void stuff_char(uint8 q);
302
+ inline uint8 get_octet();
303
+ inline uint get_bits(int num_bits);
304
+ inline uint get_bits_no_markers(int numbits);
305
+ inline int huff_decode(huff_tables *pH);
306
+ inline int huff_decode(huff_tables *pH, int& extrabits);
307
+ static inline uint8 clamp(int i);
308
+ static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
309
+ static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
310
+ static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
311
+ static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
312
+ };
313
+
314
+ } // namespace jpgd
315
+
316
+ #endif // JPEG_DECODER_H
crazy_functions/test_project/cpp/libJPG/jpge.cpp ADDED
@@ -0,0 +1,1049 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // jpge.cpp - C++ class for JPEG compression.
2
+ // Public domain, Rich Geldreich <richgel99@gmail.com>
3
+ // v1.01, Dec. 18, 2010 - Initial release
4
+ // v1.02, Apr. 6, 2011 - Removed 2x2 ordered dither in H2V1 chroma subsampling method load_block_16_8_8(). (The rounding factor was 2, when it should have been 1. Either way, it wasn't helping.)
5
+ // v1.03, Apr. 16, 2011 - Added support for optimized Huffman code tables, optimized dynamic memory allocation down to only 1 alloc.
6
+ // Also from Alex Evans: Added RGBA support, linear memory allocator (no longer needed in v1.03).
7
+ // v1.04, May. 19, 2012: Forgot to set m_pFile ptr to NULL in cfile_stream::close(). Thanks to Owen Kaluza for reporting this bug.
8
+ // Code tweaks to fix VS2008 static code analysis warnings (all looked harmless).
9
+ // Code review revealed method load_block_16_8_8() (used for the non-default H2V1 sampling mode to downsample chroma) somehow didn't get the rounding factor fix from v1.02.
10
+
11
+ #include "jpge.h"
12
+
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+ #if PLATFORM_WINDOWS
16
+ #include <malloc.h>
17
+ #endif
18
+
19
+ #define JPGE_MAX(a,b) (((a)>(b))?(a):(b))
20
+ #define JPGE_MIN(a,b) (((a)<(b))?(a):(b))
21
+
22
+ namespace jpge {
23
+
24
+ static inline void *jpge_malloc(size_t nSize) { return FMemory::Malloc(nSize); }
25
+ static inline void jpge_free(void *p) { FMemory::Free(p);; }
26
+
27
+ // Various JPEG enums and tables.
28
+ enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 };
29
+ enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 };
30
+
31
+ static uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
32
+ static int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 };
33
+ static int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 };
34
+ static uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 };
35
+ static uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
36
+ static uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d };
37
+ static uint8 s_ac_lum_val[AC_LUM_CODES] =
38
+ {
39
+ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
40
+ 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
41
+ 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
42
+ 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
43
+ 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
44
+ 0xf9,0xfa
45
+ };
46
+ static uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };
47
+ static uint8 s_dc_chroma_val[DC_CHROMA_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
48
+ static uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 };
49
+ static uint8 s_ac_chroma_val[AC_CHROMA_CODES] =
50
+ {
51
+ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
52
+ 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
53
+ 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
54
+ 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
55
+ 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
56
+ 0xf9,0xfa
57
+ };
58
+
59
+ // Low-level helper functions.
60
+ template <class T> inline void clear_obj(T &obj) { memset(&obj, 0, sizeof(obj)); }
61
+
62
+ const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
63
+ static inline uint8 clamp(int i) { if (static_cast<uint>(i) > 255U) { if (i < 0) i = 0; else if (i > 255) i = 255; } return static_cast<uint8>(i); }
64
+
65
+ static void RGB_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels)
66
+ {
67
+ for ( ; num_pixels; pDst += 3, pSrc += 3, num_pixels--)
68
+ {
69
+ const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
70
+ pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
71
+ pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
72
+ pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
73
+ }
74
+ }
75
+
76
+ static void RGB_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels)
77
+ {
78
+ for ( ; num_pixels; pDst++, pSrc += 3, num_pixels--)
79
+ pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
80
+ }
81
+
82
+ static void RGBA_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels)
83
+ {
84
+ for ( ; num_pixels; pDst += 3, pSrc += 4, num_pixels--)
85
+ {
86
+ const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
87
+ pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
88
+ pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
89
+ pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
90
+ }
91
+ }
92
+
93
+ static void RGBA_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels)
94
+ {
95
+ for ( ; num_pixels; pDst++, pSrc += 4, num_pixels--)
96
+ pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
97
+ }
98
+
99
+ static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels)
100
+ {
101
+ for( ; num_pixels; pDst += 3, pSrc++, num_pixels--) { pDst[0] = pSrc[0]; pDst[1] = 128; pDst[2] = 128; }
102
+ }
103
+
104
+ // Forward DCT - DCT derived from jfdctint.
105
+ #define CONST_BITS 13
106
+ #define ROW_BITS 2
107
+ #define DCT_DESCALE(x, n) (((x) + (((int32)1) << ((n) - 1))) >> (n))
108
+ #define DCT_MUL(var, c) (static_cast<int16>(var) * static_cast<int32>(c))
109
+ #define DCT1D(s0, s1, s2, s3, s4, s5, s6, s7) \
110
+ int32 t0 = s0 + s7, t7 = s0 - s7, t1 = s1 + s6, t6 = s1 - s6, t2 = s2 + s5, t5 = s2 - s5, t3 = s3 + s4, t4 = s3 - s4; \
111
+ int32 t10 = t0 + t3, t13 = t0 - t3, t11 = t1 + t2, t12 = t1 - t2; \
112
+ int32 u1 = DCT_MUL(t12 + t13, 4433); \
113
+ s2 = u1 + DCT_MUL(t13, 6270); \
114
+ s6 = u1 + DCT_MUL(t12, -15137); \
115
+ u1 = t4 + t7; \
116
+ int32 u2 = t5 + t6, u3 = t4 + t6, u4 = t5 + t7; \
117
+ int32 z5 = DCT_MUL(u3 + u4, 9633); \
118
+ t4 = DCT_MUL(t4, 2446); t5 = DCT_MUL(t5, 16819); \
119
+ t6 = DCT_MUL(t6, 25172); t7 = DCT_MUL(t7, 12299); \
120
+ u1 = DCT_MUL(u1, -7373); u2 = DCT_MUL(u2, -20995); \
121
+ u3 = DCT_MUL(u3, -16069); u4 = DCT_MUL(u4, -3196); \
122
+ u3 += z5; u4 += z5; \
123
+ s0 = t10 + t11; s1 = t7 + u1 + u4; s3 = t6 + u2 + u3; s4 = t10 - t11; s5 = t5 + u2 + u4; s7 = t4 + u1 + u3;
124
+
125
+ static void DCT2D(int32 *p)
126
+ {
127
+ int32 c, *q = p;
128
+ for (c = 7; c >= 0; c--, q += 8)
129
+ {
130
+ int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7];
131
+ DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
132
+ q[0] = s0 << ROW_BITS; q[1] = DCT_DESCALE(s1, CONST_BITS-ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS-ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS-ROW_BITS);
133
+ q[4] = s4 << ROW_BITS; q[5] = DCT_DESCALE(s5, CONST_BITS-ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS-ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS-ROW_BITS);
134
+ }
135
+ for (q = p, c = 7; c >= 0; c--, q++)
136
+ {
137
+ int32 s0 = q[0*8], s1 = q[1*8], s2 = q[2*8], s3 = q[3*8], s4 = q[4*8], s5 = q[5*8], s6 = q[6*8], s7 = q[7*8];
138
+ DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
139
+ q[0*8] = DCT_DESCALE(s0, ROW_BITS+3); q[1*8] = DCT_DESCALE(s1, CONST_BITS+ROW_BITS+3); q[2*8] = DCT_DESCALE(s2, CONST_BITS+ROW_BITS+3); q[3*8] = DCT_DESCALE(s3, CONST_BITS+ROW_BITS+3);
140
+ q[4*8] = DCT_DESCALE(s4, ROW_BITS+3); q[5*8] = DCT_DESCALE(s5, CONST_BITS+ROW_BITS+3); q[6*8] = DCT_DESCALE(s6, CONST_BITS+ROW_BITS+3); q[7*8] = DCT_DESCALE(s7, CONST_BITS+ROW_BITS+3);
141
+ }
142
+ }
143
+
144
+ struct sym_freq { uint m_key, m_sym_index; };
145
+
146
+ // Radix sorts sym_freq[] array by 32-bit key m_key. Returns ptr to sorted values.
147
+ static inline sym_freq* radix_sort_syms(uint num_syms, sym_freq* pSyms0, sym_freq* pSyms1)
148
+ {
149
+ const uint cMaxPasses = 4;
150
+ uint32 hist[256 * cMaxPasses]; clear_obj(hist);
151
+ for (uint i = 0; i < num_syms; i++) { uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; hist[256*2 + ((freq >> 16) & 0xFF)]++; hist[256*3 + ((freq >> 24) & 0xFF)]++; }
152
+ sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1;
153
+ uint total_passes = cMaxPasses; while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
154
+ for (uint pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
155
+ {
156
+ const uint32* pHist = &hist[pass << 8];
157
+ uint offsets[256], cur_ofs = 0;
158
+ for (uint i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
159
+ for (uint i = 0; i < num_syms; i++)
160
+ pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
161
+ sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t;
162
+ }
163
+ return pCur_syms;
164
+ }
165
+
166
+ // calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.
167
+ static void calculate_minimum_redundancy(sym_freq *A, int n)
168
+ {
169
+ int root, leaf, next, avbl, used, dpth;
170
+ if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
171
+ A[0].m_key += A[1].m_key; root = 0; leaf = 2;
172
+ for (next=1; next < n-1; next++)
173
+ {
174
+ if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = next; } else A[next].m_key = A[leaf++].m_key;
175
+ if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key += A[root].m_key; A[root++].m_key = next; } else A[next].m_key += A[leaf++].m_key;
176
+ }
177
+ A[n-2].m_key = 0;
178
+ for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
179
+ avbl = 1; used = dpth = 0; root = n-2; next = n-1;
180
+ while (avbl>0)
181
+ {
182
+ while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
183
+ while (avbl>used) { A[next--].m_key = dpth; avbl--; }
184
+ avbl = 2*used; dpth++; used = 0;
185
+ }
186
+ }
187
+
188
+ // Limits canonical Huffman code table's max code size to max_code_size.
189
+ static void huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
190
+ {
191
+ if (code_list_len <= 1) return;
192
+
193
+ for (int i = max_code_size + 1; i <= MAX_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
194
+
195
+ uint32 total = 0;
196
+ for (int i = max_code_size; i > 0; i--)
197
+ total += (((uint32)pNum_codes[i]) << (max_code_size - i));
198
+
199
+ while (total != (1UL << max_code_size))
200
+ {
201
+ pNum_codes[max_code_size]--;
202
+ for (int i = max_code_size - 1; i > 0; i--)
203
+ {
204
+ if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
205
+ }
206
+ total--;
207
+ }
208
+ }
209
+
210
+ // Generates an optimized offman table.
211
+ void jpeg_encoder::optimize_huffman_table(int table_num, int table_len)
212
+ {
213
+ sym_freq syms0[MAX_HUFF_SYMBOLS], syms1[MAX_HUFF_SYMBOLS];
214
+ syms0[0].m_key = 1; syms0[0].m_sym_index = 0; // dummy symbol, assures that no valid code contains all 1's
215
+ int num_used_syms = 1;
216
+ const uint32 *pSym_count = &m_huff_count[table_num][0];
217
+ for (int i = 0; i < table_len; i++)
218
+ if (pSym_count[i]) { syms0[num_used_syms].m_key = pSym_count[i]; syms0[num_used_syms++].m_sym_index = i + 1; }
219
+ sym_freq* pSyms = radix_sort_syms(num_used_syms, syms0, syms1);
220
+ calculate_minimum_redundancy(pSyms, num_used_syms);
221
+
222
+ // Count the # of symbols of each code size.
223
+ int num_codes[1 + MAX_HUFF_CODESIZE]; clear_obj(num_codes);
224
+ for (int i = 0; i < num_used_syms; i++)
225
+ num_codes[pSyms[i].m_key]++;
226
+
227
+ const uint JPGE_CODE_SIZE_LIMIT = 16; // the maximum possible size of a JPEG Huffman code (valid range is [9,16] - 9 vs. 8 because of the dummy symbol)
228
+ huffman_enforce_max_code_size(num_codes, num_used_syms, JPGE_CODE_SIZE_LIMIT);
229
+
230
+ // Compute m_huff_bits array, which contains the # of symbols per code size.
231
+ clear_obj(m_huff_bits[table_num]);
232
+ for (int i = 1; i <= (int)JPGE_CODE_SIZE_LIMIT; i++)
233
+ m_huff_bits[table_num][i] = static_cast<uint8>(num_codes[i]);
234
+
235
+ // Remove the dummy symbol added above, which must be in largest bucket.
236
+ for (int i = JPGE_CODE_SIZE_LIMIT; i >= 1; i--)
237
+ {
238
+ if (m_huff_bits[table_num][i]) { m_huff_bits[table_num][i]--; break; }
239
+ }
240
+
241
+ // Compute the m_huff_val array, which contains the symbol indices sorted by code size (smallest to largest).
242
+ for (int i = num_used_syms - 1; i >= 1; i--)
243
+ m_huff_val[table_num][num_used_syms - 1 - i] = static_cast<uint8>(pSyms[i].m_sym_index - 1);
244
+ }
245
+
246
+ // JPEG marker generation.
247
+ void jpeg_encoder::emit_byte(uint8 i)
248
+ {
249
+ m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_obj(i);
250
+ }
251
+
252
+ void jpeg_encoder::emit_word(uint i)
253
+ {
254
+ emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF));
255
+ }
256
+
257
+ void jpeg_encoder::emit_marker(int marker)
258
+ {
259
+ emit_byte(uint8(0xFF)); emit_byte(uint8(marker));
260
+ }
261
+
262
+ // Emit JFIF marker
263
+ void jpeg_encoder::emit_jfif_app0()
264
+ {
265
+ emit_marker(M_APP0);
266
+ emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1);
267
+ emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */
268
+ emit_byte(0);
269
+ emit_byte(1); /* Major version */
270
+ emit_byte(1); /* Minor version */
271
+ emit_byte(0); /* Density unit */
272
+ emit_word(1);
273
+ emit_word(1);
274
+ emit_byte(0); /* No thumbnail image */
275
+ emit_byte(0);
276
+ }
277
+
278
+ // Emit quantization tables
279
+ void jpeg_encoder::emit_dqt()
280
+ {
281
+ for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++)
282
+ {
283
+ emit_marker(M_DQT);
284
+ emit_word(64 + 1 + 2);
285
+ emit_byte(static_cast<uint8>(i));
286
+ for (int j = 0; j < 64; j++)
287
+ emit_byte(static_cast<uint8>(m_quantization_tables[i][j]));
288
+ }
289
+ }
290
+
291
+ // Emit start of frame marker
292
+ void jpeg_encoder::emit_sof()
293
+ {
294
+ emit_marker(M_SOF0); /* baseline */
295
+ emit_word(3 * m_num_components + 2 + 5 + 1);
296
+ emit_byte(8); /* precision */
297
+ emit_word(m_image_y);
298
+ emit_word(m_image_x);
299
+ emit_byte(m_num_components);
300
+ for (int i = 0; i < m_num_components; i++)
301
+ {
302
+ emit_byte(static_cast<uint8>(i + 1)); /* component ID */
303
+ emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]); /* h and v sampling */
304
+ emit_byte(i > 0); /* quant. table num */
305
+ }
306
+ }
307
+
308
+ // Emit Huffman table.
309
+ void jpeg_encoder::emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag)
310
+ {
311
+ emit_marker(M_DHT);
312
+
313
+ int length = 0;
314
+ for (int i = 1; i <= 16; i++)
315
+ length += bits[i];
316
+
317
+ emit_word(length + 2 + 1 + 16);
318
+ emit_byte(static_cast<uint8>(index + (ac_flag << 4)));
319
+
320
+ for (int i = 1; i <= 16; i++)
321
+ emit_byte(bits[i]);
322
+
323
+ for (int i = 0; i < length; i++)
324
+ emit_byte(val[i]);
325
+ }
326
+
327
+ // Emit all Huffman tables.
328
+ void jpeg_encoder::emit_dhts()
329
+ {
330
+ emit_dht(m_huff_bits[0+0], m_huff_val[0+0], 0, false);
331
+ emit_dht(m_huff_bits[2+0], m_huff_val[2+0], 0, true);
332
+ if (m_num_components == 3)
333
+ {
334
+ emit_dht(m_huff_bits[0+1], m_huff_val[0+1], 1, false);
335
+ emit_dht(m_huff_bits[2+1], m_huff_val[2+1], 1, true);
336
+ }
337
+ }
338
+
339
+ // emit start of scan
340
+ void jpeg_encoder::emit_sos()
341
+ {
342
+ emit_marker(M_SOS);
343
+ emit_word(2 * m_num_components + 2 + 1 + 3);
344
+ emit_byte(m_num_components);
345
+ for (int i = 0; i < m_num_components; i++)
346
+ {
347
+ emit_byte(static_cast<uint8>(i + 1));
348
+ if (i == 0)
349
+ emit_byte((0 << 4) + 0);
350
+ else
351
+ emit_byte((1 << 4) + 1);
352
+ }
353
+ emit_byte(0); /* spectral selection */
354
+ emit_byte(63);
355
+ emit_byte(0);
356
+ }
357
+
358
+ // Emit all markers at beginning of image file.
359
+ void jpeg_encoder::emit_markers()
360
+ {
361
+ emit_marker(M_SOI);
362
+ emit_jfif_app0();
363
+ emit_dqt();
364
+ emit_sof();
365
+ emit_dhts();
366
+ emit_sos();
367
+ }
368
+
369
+ // Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays.
370
+ void jpeg_encoder::compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val)
371
+ {
372
+ int i, l, last_p, si;
373
+ uint8 huff_size[257];
374
+ uint huff_code[257];
375
+ uint code;
376
+
377
+ int p = 0;
378
+ for (l = 1; l <= 16; l++)
379
+ for (i = 1; i <= bits[l]; i++)
380
+ huff_size[p++] = (char)l;
381
+
382
+ huff_size[p] = 0; last_p = p; // write sentinel
383
+
384
+ code = 0; si = huff_size[0]; p = 0;
385
+
386
+ while (huff_size[p])
387
+ {
388
+ while (huff_size[p] == si)
389
+ huff_code[p++] = code++;
390
+ code <<= 1;
391
+ si++;
392
+ }
393
+
394
+ memset(codes, 0, sizeof(codes[0])*256);
395
+ memset(code_sizes, 0, sizeof(code_sizes[0])*256);
396
+ for (p = 0; p < last_p; p++)
397
+ {
398
+ codes[val[p]] = huff_code[p];
399
+ code_sizes[val[p]] = huff_size[p];
400
+ }
401
+ }
402
+
403
+ // Quantization table generation.
404
+ void jpeg_encoder::compute_quant_table(int32 *pDst, int16 *pSrc)
405
+ {
406
+ int32 q;
407
+ if (m_params.m_quality < 50)
408
+ q = 5000 / m_params.m_quality;
409
+ else
410
+ q = 200 - m_params.m_quality * 2;
411
+ for (int i = 0; i < 64; i++)
412
+ {
413
+ int32 j = *pSrc++; j = (j * q + 50L) / 100L;
414
+ *pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255);
415
+ }
416
+ }
417
+
418
+ // Higher-level methods.
419
+ void jpeg_encoder::first_pass_init()
420
+ {
421
+ m_bit_buffer = 0; m_bits_in = 0;
422
+ memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0]));
423
+ m_mcu_y_ofs = 0;
424
+ m_pass_num = 1;
425
+ }
426
+
427
+ bool jpeg_encoder::second_pass_init()
428
+ {
429
+ compute_huffman_table(&m_huff_codes[0+0][0], &m_huff_code_sizes[0+0][0], m_huff_bits[0+0], m_huff_val[0+0]);
430
+ compute_huffman_table(&m_huff_codes[2+0][0], &m_huff_code_sizes[2+0][0], m_huff_bits[2+0], m_huff_val[2+0]);
431
+ if (m_num_components > 1)
432
+ {
433
+ compute_huffman_table(&m_huff_codes[0+1][0], &m_huff_code_sizes[0+1][0], m_huff_bits[0+1], m_huff_val[0+1]);
434
+ compute_huffman_table(&m_huff_codes[2+1][0], &m_huff_code_sizes[2+1][0], m_huff_bits[2+1], m_huff_val[2+1]);
435
+ }
436
+ first_pass_init();
437
+ emit_markers();
438
+ m_pass_num = 2;
439
+ return true;
440
+ }
441
+
442
+ bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels)
443
+ {
444
+ m_num_components = 3;
445
+ switch (m_params.m_subsampling)
446
+ {
447
+ case Y_ONLY:
448
+ {
449
+ m_num_components = 1;
450
+ m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
451
+ m_mcu_x = 8; m_mcu_y = 8;
452
+ break;
453
+ }
454
+ case H1V1:
455
+ {
456
+ m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
457
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
458
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
459
+ m_mcu_x = 8; m_mcu_y = 8;
460
+ break;
461
+ }
462
+ case H2V1:
463
+ {
464
+ m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1;
465
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
466
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
467
+ m_mcu_x = 16; m_mcu_y = 8;
468
+ break;
469
+ }
470
+ case H2V2:
471
+ {
472
+ m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2;
473
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
474
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
475
+ m_mcu_x = 16; m_mcu_y = 16;
476
+ }
477
+ }
478
+
479
+ m_image_x = p_x_res; m_image_y = p_y_res;
480
+ m_image_bpp = src_channels;
481
+ m_image_bpl = m_image_x * src_channels;
482
+ m_image_x_mcu = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1));
483
+ m_image_y_mcu = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1));
484
+ m_image_bpl_xlt = m_image_x * m_num_components;
485
+ m_image_bpl_mcu = m_image_x_mcu * m_num_components;
486
+ m_mcus_per_row = m_image_x_mcu / m_mcu_x;
487
+
488
+ if ((m_mcu_lines[0] = static_cast<uint8*>(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) return false;
489
+ for (int i = 1; i < m_mcu_y; i++)
490
+ m_mcu_lines[i] = m_mcu_lines[i-1] + m_image_bpl_mcu;
491
+
492
+ compute_quant_table(m_quantization_tables[0], s_std_lum_quant);
493
+ compute_quant_table(m_quantization_tables[1], m_params.m_no_chroma_discrim_flag ? s_std_lum_quant : s_std_croma_quant);
494
+
495
+ m_out_buf_left = JPGE_OUT_BUF_SIZE;
496
+ m_pOut_buf = m_out_buf;
497
+
498
+ if (m_params.m_two_pass_flag)
499
+ {
500
+ clear_obj(m_huff_count);
501
+ first_pass_init();
502
+ }
503
+ else
504
+ {
505
+ memcpy(m_huff_bits[0+0], s_dc_lum_bits, 17); memcpy(m_huff_val [0+0], s_dc_lum_val, DC_LUM_CODES);
506
+ memcpy(m_huff_bits[2+0], s_ac_lum_bits, 17); memcpy(m_huff_val [2+0], s_ac_lum_val, AC_LUM_CODES);
507
+ memcpy(m_huff_bits[0+1], s_dc_chroma_bits, 17); memcpy(m_huff_val [0+1], s_dc_chroma_val, DC_CHROMA_CODES);
508
+ memcpy(m_huff_bits[2+1], s_ac_chroma_bits, 17); memcpy(m_huff_val [2+1], s_ac_chroma_val, AC_CHROMA_CODES);
509
+ if (!second_pass_init()) return false; // in effect, skip over the first pass
510
+ }
511
+ return m_all_stream_writes_succeeded;
512
+ }
513
+
514
+ void jpeg_encoder::load_block_8_8_grey(int x)
515
+ {
516
+ uint8 *pSrc;
517
+ sample_array_t *pDst = m_sample_array;
518
+ x <<= 3;
519
+ for (int i = 0; i < 8; i++, pDst += 8)
520
+ {
521
+ pSrc = m_mcu_lines[i] + x;
522
+ pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128;
523
+ pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128;
524
+ }
525
+ }
526
+
527
+ void jpeg_encoder::load_block_8_8(int x, int y, int c)
528
+ {
529
+ uint8 *pSrc;
530
+ sample_array_t *pDst = m_sample_array;
531
+ x = (x * (8 * 3)) + c;
532
+ y <<= 3;
533
+ for (int i = 0; i < 8; i++, pDst += 8)
534
+ {
535
+ pSrc = m_mcu_lines[y + i] + x;
536
+ pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128;
537
+ pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128;
538
+ }
539
+ }
540
+
541
+ void jpeg_encoder::load_block_16_8(int x, int c)
542
+ {
543
+ uint8 *pSrc1, *pSrc2;
544
+ sample_array_t *pDst = m_sample_array;
545
+ x = (x * (16 * 3)) + c;
546
+ int a = 0, b = 2;
547
+ for (int i = 0; i < 16; i += 2, pDst += 8)
548
+ {
549
+ pSrc1 = m_mcu_lines[i + 0] + x;
550
+ pSrc2 = m_mcu_lines[i + 1] + x;
551
+ pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3] + pSrc2[ 0 * 3] + pSrc2[ 1 * 3] + a) >> 2) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3] + pSrc2[ 2 * 3] + pSrc2[ 3 * 3] + b) >> 2) - 128;
552
+ pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3] + pSrc2[ 4 * 3] + pSrc2[ 5 * 3] + a) >> 2) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3] + pSrc2[ 6 * 3] + pSrc2[ 7 * 3] + b) >> 2) - 128;
553
+ pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3] + pSrc2[ 8 * 3] + pSrc2[ 9 * 3] + a) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + b) >> 2) - 128;
554
+ pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + a) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + b) >> 2) - 128;
555
+ int temp = a; a = b; b = temp;
556
+ }
557
+ }
558
+
559
+ void jpeg_encoder::load_block_16_8_8(int x, int c)
560
+ {
561
+ uint8 *pSrc1;
562
+ sample_array_t *pDst = m_sample_array;
563
+ x = (x * (16 * 3)) + c;
564
+ for (int i = 0; i < 8; i++, pDst += 8)
565
+ {
566
+ pSrc1 = m_mcu_lines[i + 0] + x;
567
+ pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3]) >> 1) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3]) >> 1) - 128;
568
+ pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3]) >> 1) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3]) >> 1) - 128;
569
+ pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3]) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3]) >> 1) - 128;
570
+ pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3]) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3]) >> 1) - 128;
571
+ }
572
+ }
573
+
574
+ void jpeg_encoder::load_quantized_coefficients(int component_num)
575
+ {
576
+ int32 *q = m_quantization_tables[component_num > 0];
577
+ int16 *pDst = m_coefficient_array;
578
+ for (int i = 0; i < 64; i++)
579
+ {
580
+ sample_array_t j = m_sample_array[s_zag[i]];
581
+ if (j < 0)
582
+ {
583
+ if ((j = -j + (*q >> 1)) < *q)
584
+ *pDst++ = 0;
585
+ else
586
+ *pDst++ = static_cast<int16>(-(j / *q));
587
+ }
588
+ else
589
+ {
590
+ if ((j = j + (*q >> 1)) < *q)
591
+ *pDst++ = 0;
592
+ else
593
+ *pDst++ = static_cast<int16>((j / *q));
594
+ }
595
+ q++;
596
+ }
597
+ }
598
+
599
+ void jpeg_encoder::flush_output_buffer()
600
+ {
601
+ if (m_out_buf_left != JPGE_OUT_BUF_SIZE)
602
+ m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left);
603
+ m_pOut_buf = m_out_buf;
604
+ m_out_buf_left = JPGE_OUT_BUF_SIZE;
605
+ }
606
+
607
+ void jpeg_encoder::put_bits(uint bits, uint len)
608
+ {
609
+ m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len)));
610
+ while (m_bits_in >= 8)
611
+ {
612
+ uint8 c;
613
+ #define JPGE_PUT_BYTE(c) { *m_pOut_buf++ = (c); if (--m_out_buf_left == 0) flush_output_buffer(); }
614
+ JPGE_PUT_BYTE(c = (uint8)((m_bit_buffer >> 16) & 0xFF));
615
+ if (c == 0xFF) JPGE_PUT_BYTE(0);
616
+ m_bit_buffer <<= 8;
617
+ m_bits_in -= 8;
618
+ }
619
+ }
620
+
621
+ void jpeg_encoder::code_coefficients_pass_one(int component_num)
622
+ {
623
+ if (component_num >= 3) return; // just to shut up static analysis
624
+ int i, run_len, nbits, temp1;
625
+ int16 *src = m_coefficient_array;
626
+ uint32 *dc_count = component_num ? m_huff_count[0 + 1] : m_huff_count[0 + 0], *ac_count = component_num ? m_huff_count[2 + 1] : m_huff_count[2 + 0];
627
+
628
+ temp1 = src[0] - m_last_dc_val[component_num];
629
+ m_last_dc_val[component_num] = src[0];
630
+ if (temp1 < 0) temp1 = -temp1;
631
+
632
+ nbits = 0;
633
+ while (temp1)
634
+ {
635
+ nbits++; temp1 >>= 1;
636
+ }
637
+
638
+ dc_count[nbits]++;
639
+ for (run_len = 0, i = 1; i < 64; i++)
640
+ {
641
+ if ((temp1 = m_coefficient_array[i]) == 0)
642
+ run_len++;
643
+ else
644
+ {
645
+ while (run_len >= 16)
646
+ {
647
+ ac_count[0xF0]++;
648
+ run_len -= 16;
649
+ }
650
+ if (temp1 < 0) temp1 = -temp1;
651
+ nbits = 1;
652
+ while (temp1 >>= 1) nbits++;
653
+ ac_count[(run_len << 4) + nbits]++;
654
+ run_len = 0;
655
+ }
656
+ }
657
+ if (run_len) ac_count[0]++;
658
+ }
659
+
660
+ void jpeg_encoder::code_coefficients_pass_two(int component_num)
661
+ {
662
+ int i, j, run_len, nbits, temp1, temp2;
663
+ int16 *pSrc = m_coefficient_array;
664
+ uint *codes[2];
665
+ uint8 *code_sizes[2];
666
+
667
+ if (component_num == 0)
668
+ {
669
+ codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0];
670
+ code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0];
671
+ }
672
+ else
673
+ {
674
+ codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1];
675
+ code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1];
676
+ }
677
+
678
+ temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num];
679
+ m_last_dc_val[component_num] = pSrc[0];
680
+
681
+ if (temp1 < 0)
682
+ {
683
+ temp1 = -temp1; temp2--;
684
+ }
685
+
686
+ nbits = 0;
687
+ while (temp1)
688
+ {
689
+ nbits++; temp1 >>= 1;
690
+ }
691
+
692
+ put_bits(codes[0][nbits], code_sizes[0][nbits]);
693
+ if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits);
694
+
695
+ for (run_len = 0, i = 1; i < 64; i++)
696
+ {
697
+ if ((temp1 = m_coefficient_array[i]) == 0)
698
+ run_len++;
699
+ else
700
+ {
701
+ while (run_len >= 16)
702
+ {
703
+ put_bits(codes[1][0xF0], code_sizes[1][0xF0]);
704
+ run_len -= 16;
705
+ }
706
+ if ((temp2 = temp1) < 0)
707
+ {
708
+ temp1 = -temp1;
709
+ temp2--;
710
+ }
711
+ nbits = 1;
712
+ while (temp1 >>= 1)
713
+ nbits++;
714
+ j = (run_len << 4) + nbits;
715
+ put_bits(codes[1][j], code_sizes[1][j]);
716
+ put_bits(temp2 & ((1 << nbits) - 1), nbits);
717
+ run_len = 0;
718
+ }
719
+ }
720
+ if (run_len)
721
+ put_bits(codes[1][0], code_sizes[1][0]);
722
+ }
723
+
724
+ void jpeg_encoder::code_block(int component_num)
725
+ {
726
+ DCT2D(m_sample_array);
727
+ load_quantized_coefficients(component_num);
728
+ if (m_pass_num == 1)
729
+ code_coefficients_pass_one(component_num);
730
+ else
731
+ code_coefficients_pass_two(component_num);
732
+ }
733
+
734
+ void jpeg_encoder::process_mcu_row()
735
+ {
736
+ if (m_num_components == 1)
737
+ {
738
+ for (int i = 0; i < m_mcus_per_row; i++)
739
+ {
740
+ load_block_8_8_grey(i); code_block(0);
741
+ }
742
+ }
743
+ else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
744
+ {
745
+ for (int i = 0; i < m_mcus_per_row; i++)
746
+ {
747
+ load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2);
748
+ }
749
+ }
750
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
751
+ {
752
+ for (int i = 0; i < m_mcus_per_row; i++)
753
+ {
754
+ load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
755
+ load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2);
756
+ }
757
+ }
758
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
759
+ {
760
+ for (int i = 0; i < m_mcus_per_row; i++)
761
+ {
762
+ load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
763
+ load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0);
764
+ load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2);
765
+ }
766
+ }
767
+ }
768
+
769
+ bool jpeg_encoder::terminate_pass_one()
770
+ {
771
+ optimize_huffman_table(0+0, DC_LUM_CODES); optimize_huffman_table(2+0, AC_LUM_CODES);
772
+ if (m_num_components > 1)
773
+ {
774
+ optimize_huffman_table(0+1, DC_CHROMA_CODES); optimize_huffman_table(2+1, AC_CHROMA_CODES);
775
+ }
776
+ return second_pass_init();
777
+ }
778
+
779
+ bool jpeg_encoder::terminate_pass_two()
780
+ {
781
+ put_bits(0x7F, 7);
782
+ flush_output_buffer();
783
+ emit_marker(M_EOI);
784
+ m_pass_num++; // purposely bump up m_pass_num, for debugging
785
+ return true;
786
+ }
787
+
788
+ bool jpeg_encoder::process_end_of_image()
789
+ {
790
+ if (m_mcu_y_ofs)
791
+ {
792
+ if (m_mcu_y_ofs < 16) // check here just to shut up static analysis
793
+ {
794
+ for (int i = m_mcu_y_ofs; i < m_mcu_y; i++)
795
+ memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu);
796
+ }
797
+
798
+ process_mcu_row();
799
+ }
800
+
801
+ if (m_pass_num == 1)
802
+ return terminate_pass_one();
803
+ else
804
+ return terminate_pass_two();
805
+ }
806
+
807
+ void jpeg_encoder::load_mcu(const void *pSrc)
808
+ {
809
+ const uint8* Psrc = reinterpret_cast<const uint8*>(pSrc);
810
+
811
+ uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst
812
+
813
+ if (m_num_components == 1)
814
+ {
815
+ if (m_image_bpp == 4)
816
+ RGBA_to_Y(pDst, Psrc, m_image_x);
817
+ else if (m_image_bpp == 3)
818
+ RGB_to_Y(pDst, Psrc, m_image_x);
819
+ else
820
+ memcpy(pDst, Psrc, m_image_x);
821
+ }
822
+ else
823
+ {
824
+ if (m_image_bpp == 4)
825
+ RGBA_to_YCC(pDst, Psrc, m_image_x);
826
+ else if (m_image_bpp == 3)
827
+ RGB_to_YCC(pDst, Psrc, m_image_x);
828
+ else
829
+ Y_to_YCC(pDst, Psrc, m_image_x);
830
+ }
831
+
832
+ // Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16
833
+ if (m_num_components == 1)
834
+ memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x);
835
+ else
836
+ {
837
+ const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2];
838
+ uint8 *q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt;
839
+ for (int i = m_image_x; i < m_image_x_mcu; i++)
840
+ {
841
+ *q++ = y; *q++ = cb; *q++ = cr;
842
+ }
843
+ }
844
+
845
+ if (++m_mcu_y_ofs == m_mcu_y)
846
+ {
847
+ process_mcu_row();
848
+ m_mcu_y_ofs = 0;
849
+ }
850
+ }
851
+
852
+ void jpeg_encoder::clear()
853
+ {
854
+ m_mcu_lines[0] = NULL;
855
+ m_pass_num = 0;
856
+ m_all_stream_writes_succeeded = true;
857
+ }
858
+
859
+ jpeg_encoder::jpeg_encoder()
860
+ {
861
+ clear();
862
+ }
863
+
864
+ jpeg_encoder::~jpeg_encoder()
865
+ {
866
+ deinit();
867
+ }
868
+
869
+ bool jpeg_encoder::init(output_stream *pStream, int64_t width, int64_t height, int64_t src_channels, const params &comp_params)
870
+ {
871
+ deinit();
872
+ if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check_valid())) return false;
873
+ m_pStream = pStream;
874
+ m_params = comp_params;
875
+ return jpg_open(width, height, src_channels);
876
+ }
877
+
878
+ void jpeg_encoder::deinit()
879
+ {
880
+ jpge_free(m_mcu_lines[0]);
881
+ clear();
882
+ }
883
+
884
+ bool jpeg_encoder::process_scanline(const void* pScanline)
885
+ {
886
+ if ((m_pass_num < 1) || (m_pass_num > 2)) return false;
887
+ if (m_all_stream_writes_succeeded)
888
+ {
889
+ if (!pScanline)
890
+ {
891
+ if (!process_end_of_image()) return false;
892
+ }
893
+ else
894
+ {
895
+ load_mcu(pScanline);
896
+ }
897
+ }
898
+ return m_all_stream_writes_succeeded;
899
+ }
900
+
901
+ // Higher level wrappers/examples (optional).
902
+ #include <stdio.h>
903
+
904
+ class cfile_stream : public output_stream
905
+ {
906
+ cfile_stream(const cfile_stream &);
907
+ cfile_stream &operator= (const cfile_stream &);
908
+
909
+ FILE* m_pFile;
910
+ bool m_bStatus;
911
+
912
+ public:
913
+ cfile_stream() : m_pFile(NULL), m_bStatus(false) { }
914
+
915
+ virtual ~cfile_stream()
916
+ {
917
+ close();
918
+ }
919
+
920
+ bool open(const char *pFilename)
921
+ {
922
+ close();
923
+ #if defined(_MSC_VER)
924
+ if (fopen_s(&m_pFile, pFilename, "wb") != 0)
925
+ {
926
+ return false;
927
+ }
928
+ #else
929
+ m_pFile = fopen(pFilename, "wb");
930
+ #endif
931
+ m_bStatus = (m_pFile != NULL);
932
+ return m_bStatus;
933
+ }
934
+
935
+ bool close()
936
+ {
937
+ if (m_pFile)
938
+ {
939
+ if (fclose(m_pFile) == EOF)
940
+ {
941
+ m_bStatus = false;
942
+ }
943
+ m_pFile = NULL;
944
+ }
945
+ return m_bStatus;
946
+ }
947
+
948
+ virtual bool put_buf(const void* pBuf, int64_t len)
949
+ {
950
+ m_bStatus = m_bStatus && (fwrite(pBuf, len, 1, m_pFile) == 1);
951
+ return m_bStatus;
952
+ }
953
+
954
+ uint get_size() const
955
+ {
956
+ return m_pFile ? ftell(m_pFile) : 0;
957
+ }
958
+ };
959
+
960
+ // Writes JPEG image to file.
961
+ bool compress_image_to_jpeg_file(const char *pFilename, int64_t width, int64_t height, int64_t num_channels, const uint8 *pImage_data, const params &comp_params)
962
+ {
963
+ cfile_stream dst_stream;
964
+ if (!dst_stream.open(pFilename))
965
+ return false;
966
+
967
+ jpge::jpeg_encoder dst_image;
968
+ if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params))
969
+ return false;
970
+
971
+ for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++)
972
+ {
973
+ for (int64_t i = 0; i < height; i++)
974
+ {
975
+ // i, width, and num_channels are all 64bit
976
+ const uint8* pBuf = pImage_data + i * width * num_channels;
977
+ if (!dst_image.process_scanline(pBuf))
978
+ return false;
979
+ }
980
+ if (!dst_image.process_scanline(NULL))
981
+ return false;
982
+ }
983
+
984
+ dst_image.deinit();
985
+
986
+ return dst_stream.close();
987
+ }
988
+
989
+ class memory_stream : public output_stream
990
+ {
991
+ memory_stream(const memory_stream &);
992
+ memory_stream &operator= (const memory_stream &);
993
+
994
+ uint8 *m_pBuf;
995
+ uint64_t m_buf_size, m_buf_ofs;
996
+
997
+ public:
998
+ memory_stream(void *pBuf, uint64_t buf_size) : m_pBuf(static_cast<uint8*>(pBuf)), m_buf_size(buf_size), m_buf_ofs(0) { }
999
+
1000
+ virtual ~memory_stream() { }
1001
+
1002
+ virtual bool put_buf(const void* pBuf, int64_t len)
1003
+ {
1004
+ uint64_t buf_remaining = m_buf_size - m_buf_ofs;
1005
+ if ((uint64_t)len > buf_remaining)
1006
+ return false;
1007
+ memcpy(m_pBuf + m_buf_ofs, pBuf, len);
1008
+ m_buf_ofs += len;
1009
+ return true;
1010
+ }
1011
+
1012
+ uint64_t get_size() const
1013
+ {
1014
+ return m_buf_ofs;
1015
+ }
1016
+ };
1017
+
1018
+ bool compress_image_to_jpeg_file_in_memory(void *pDstBuf, int64_t &buf_size, int64_t width, int64_t height, int64_t num_channels, const uint8 *pImage_data, const params &comp_params)
1019
+ {
1020
+ if ((!pDstBuf) || (!buf_size))
1021
+ return false;
1022
+
1023
+ memory_stream dst_stream(pDstBuf, buf_size);
1024
+
1025
+ buf_size = 0;
1026
+
1027
+ jpge::jpeg_encoder dst_image;
1028
+ if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params))
1029
+ return false;
1030
+
1031
+ for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++)
1032
+ {
1033
+ for (int64_t i = 0; i < height; i++)
1034
+ {
1035
+ const uint8* pScanline = pImage_data + i * width * num_channels;
1036
+ if (!dst_image.process_scanline(pScanline))
1037
+ return false;
1038
+ }
1039
+ if (!dst_image.process_scanline(NULL))
1040
+ return false;
1041
+ }
1042
+
1043
+ dst_image.deinit();
1044
+
1045
+ buf_size = dst_stream.get_size();
1046
+ return true;
1047
+ }
1048
+
1049
+ } // namespace jpge
crazy_functions/test_project/cpp/libJPG/jpge.h ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ // jpge.h - C++ class for JPEG compression.
3
+ // Public domain, Rich Geldreich <richgel99@gmail.com>
4
+ // Alex Evans: Added RGBA support, linear memory allocator.
5
+ #ifndef JPEG_ENCODER_H
6
+ #define JPEG_ENCODER_H
7
+
8
+ #include <stdint.h>
9
+
10
+ namespace jpge
11
+ {
12
+ typedef unsigned char uint8;
13
+ typedef signed short int16;
14
+ typedef signed int int32;
15
+ typedef unsigned short uint16;
16
+ typedef unsigned int uint32;
17
+ typedef unsigned int uint;
18
+
19
+ // JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common.
20
+ enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 };
21
+
22
+ // JPEG compression parameters structure.
23
+ struct params
24
+ {
25
+ inline params() : m_quality(85), m_subsampling(H2V2), m_no_chroma_discrim_flag(false), m_two_pass_flag(false) { }
26
+
27
+ inline bool check_valid() const
28
+ {
29
+ if ((m_quality < 1) || (m_quality > 100)) return false;
30
+ if ((uint)m_subsampling > (uint)H2V2) return false;
31
+ return true;
32
+ }
33
+
34
+ // Quality: 1-100, higher is better. Typical values are around 50-95.
35
+ int m_quality;
36
+
37
+ // m_subsampling:
38
+ // 0 = Y (grayscale) only
39
+ // 1 = YCbCr, no subsampling (H1V1, YCbCr 1x1x1, 3 blocks per MCU)
40
+ // 2 = YCbCr, H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU)
41
+ // 3 = YCbCr, H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common)
42
+ subsampling_t m_subsampling;
43
+
44
+ // Disables CbCr discrimination - only intended for testing.
45
+ // If true, the Y quantization table is also used for the CbCr channels.
46
+ bool m_no_chroma_discrim_flag;
47
+
48
+ bool m_two_pass_flag;
49
+ };
50
+
51
+ // Writes JPEG image to a file.
52
+ // num_channels must be 1 (Y) or 3 (RGB), image pitch must be width*num_channels.
53
+ bool compress_image_to_jpeg_file(const char *pFilename, int64_t width, int64_t height, int64_t num_channels, const uint8 *pImage_data, const params &comp_params = params());
54
+
55
+ // Writes JPEG image to memory buffer.
56
+ // On entry, buf_size is the size of the output buffer pointed at by pBuf, which should be at least ~1024 bytes.
57
+ // If return value is true, buf_size will be set to the size of the compressed data.
58
+ bool compress_image_to_jpeg_file_in_memory(void *pBuf, int64_t &buf_size, int64_t width, int64_t height, int64_t num_channels, const uint8 *pImage_data, const params &comp_params = params());
59
+
60
+ // Output stream abstract class - used by the jpeg_encoder class to write to the output stream.
61
+ // put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts.
62
+ class output_stream
63
+ {
64
+ public:
65
+ virtual ~output_stream() { };
66
+ virtual bool put_buf(const void* Pbuf, int64_t len) = 0;
67
+ template<class T> inline bool put_obj(const T& obj) { return put_buf(&obj, sizeof(T)); }
68
+ };
69
+
70
+ // Lower level jpeg_encoder class - useful if more control is needed than the above helper functions.
71
+ class jpeg_encoder
72
+ {
73
+ public:
74
+ jpeg_encoder();
75
+ ~jpeg_encoder();
76
+
77
+ // Initializes the compressor.
78
+ // pStream: The stream object to use for writing compressed data.
79
+ // params - Compression parameters structure, defined above.
80
+ // width, height - Image dimensions.
81
+ // channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data.
82
+ // Returns false on out of memory or if a stream write fails.
83
+ bool init(output_stream *pStream, int64_t width, int64_t height, int64_t src_channels, const params &comp_params = params());
84
+
85
+ const params &get_params() const { return m_params; }
86
+
87
+ // Deinitializes the compressor, freeing any allocated memory. May be called at any time.
88
+ void deinit();
89
+
90
+ uint get_total_passes() const { return m_params.m_two_pass_flag ? 2 : 1; }
91
+ inline uint get_cur_pass() { return m_pass_num; }
92
+
93
+ // Call this method with each source scanline.
94
+ // width * src_channels bytes per scanline is expected (RGB or Y format).
95
+ // You must call with NULL after all scanlines are processed to finish compression.
96
+ // Returns false on out of memory or if a stream write fails.
97
+ bool process_scanline(const void* pScanline);
98
+
99
+ private:
100
+ jpeg_encoder(const jpeg_encoder &);
101
+ jpeg_encoder &operator =(const jpeg_encoder &);
102
+
103
+ typedef int32 sample_array_t;
104
+
105
+ output_stream *m_pStream;
106
+ params m_params;
107
+ uint8 m_num_components;
108
+ uint8 m_comp_h_samp[3], m_comp_v_samp[3];
109
+ int m_image_x, m_image_y, m_image_bpp, m_image_bpl;
110
+ int m_image_x_mcu, m_image_y_mcu;
111
+ int m_image_bpl_xlt, m_image_bpl_mcu;
112
+ int m_mcus_per_row;
113
+ int m_mcu_x, m_mcu_y;
114
+ uint8 *m_mcu_lines[16];
115
+ uint8 m_mcu_y_ofs;
116
+ sample_array_t m_sample_array[64];
117
+ int16 m_coefficient_array[64];
118
+ int32 m_quantization_tables[2][64];
119
+ uint m_huff_codes[4][256];
120
+ uint8 m_huff_code_sizes[4][256];
121
+ uint8 m_huff_bits[4][17];
122
+ uint8 m_huff_val[4][256];
123
+ uint32 m_huff_count[4][256];
124
+ int m_last_dc_val[3];
125
+ enum { JPGE_OUT_BUF_SIZE = 2048 };
126
+ uint8 m_out_buf[JPGE_OUT_BUF_SIZE];
127
+ uint8 *m_pOut_buf;
128
+ uint m_out_buf_left;
129
+ uint32 m_bit_buffer;
130
+ uint m_bits_in;
131
+ uint8 m_pass_num;
132
+ bool m_all_stream_writes_succeeded;
133
+
134
+ void optimize_huffman_table(int table_num, int table_len);
135
+ void emit_byte(uint8 i);
136
+ void emit_word(uint i);
137
+ void emit_marker(int marker);
138
+ void emit_jfif_app0();
139
+ void emit_dqt();
140
+ void emit_sof();
141
+ void emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag);
142
+ void emit_dhts();
143
+ void emit_sos();
144
+ void emit_markers();
145
+ void compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val);
146
+ void compute_quant_table(int32 *dst, int16 *src);
147
+ void adjust_quant_table(int32 *dst, int32 *src);
148
+ void first_pass_init();
149
+ bool second_pass_init();
150
+ bool jpg_open(int p_x_res, int p_y_res, int src_channels);
151
+ void load_block_8_8_grey(int x);
152
+ void load_block_8_8(int x, int y, int c);
153
+ void load_block_16_8(int x, int c);
154
+ void load_block_16_8_8(int x, int c);
155
+ void load_quantized_coefficients(int component_num);
156
+ void flush_output_buffer();
157
+ void put_bits(uint bits, uint len);
158
+ void code_coefficients_pass_one(int component_num);
159
+ void code_coefficients_pass_two(int component_num);
160
+ void code_block(int component_num);
161
+ void process_mcu_row();
162
+ bool terminate_pass_one();
163
+ bool terminate_pass_two();
164
+ bool process_end_of_image();
165
+ void load_mcu(const void* src);
166
+ void clear();
167
+ void init();
168
+ };
169
+
170
+ } // namespace jpge
171
+
172
+ #endif // JPEG_ENCODER
crazy_functions/test_project/cpp/libJPG/来源 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ jpge.h - C++ class for JPEG compression.
2
+ Public domain, Rich Geldreich <richgel99@gmail.com>
3
+ Alex Evans: Added RGBA support, linear memory allocator.
crazy_functions/test_project/cpp/longcode/jpgd.cpp ADDED
@@ -0,0 +1,3276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // jpgd.cpp - C++ class for JPEG decompression.
2
+ // Public domain, Rich Geldreich <richgel99@gmail.com>
3
+ // Last updated Apr. 16, 2011
4
+ // Alex Evans: Linear memory allocator (taken from jpge.h).
5
+ //
6
+ // Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2.
7
+ //
8
+ // Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling.
9
+ // Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain"
10
+ // http://vision.ai.uiuc.edu/~dugad/research/dct/index.html
11
+
12
+ #include "jpgd.h"
13
+ #include <string.h>
14
+
15
+ #include <assert.h>
16
+ // BEGIN EPIC MOD
17
+ #define JPGD_ASSERT(x) { assert(x); CA_ASSUME(x); } (void)0
18
+ // END EPIC MOD
19
+
20
+ #ifdef _MSC_VER
21
+ #pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
22
+ #endif
23
+
24
+ // Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling).
25
+ // This is slower, but results in higher quality on images with highly saturated colors.
26
+ #define JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING 1
27
+
28
+ #define JPGD_TRUE (1)
29
+ #define JPGD_FALSE (0)
30
+
31
+ #define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b))
32
+ #define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b))
33
+
34
+ namespace jpgd {
35
+
36
+ static inline void *jpgd_malloc(size_t nSize) { return FMemory::Malloc(nSize); }
37
+ static inline void jpgd_free(void *p) { FMemory::Free(p); }
38
+
39
+ // BEGIN EPIC MOD
40
+ //@UE3 - use UE3 BGRA encoding instead of assuming RGBA
41
+ // stolen from IImageWrapper.h
42
+ enum ERGBFormatJPG
43
+ {
44
+ Invalid = -1,
45
+ RGBA = 0,
46
+ BGRA = 1,
47
+ Gray = 2,
48
+ };
49
+ static ERGBFormatJPG jpg_format;
50
+ // END EPIC MOD
51
+
52
+ // DCT coefficients are stored in this sequence.
53
+ static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
54
+
55
+ enum JPEG_MARKER
56
+ {
57
+ M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8,
58
+ M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC,
59
+ M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7,
60
+ M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF,
61
+ M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0
62
+ };
63
+
64
+ enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 };
65
+
66
+ #define CONST_BITS 13
67
+ #define PASS1_BITS 2
68
+ #define SCALEDONE ((int32)1)
69
+
70
+ #define FIX_0_298631336 ((int32)2446) /* FIX(0.298631336) */
71
+ #define FIX_0_390180644 ((int32)3196) /* FIX(0.390180644) */
72
+ #define FIX_0_541196100 ((int32)4433) /* FIX(0.541196100) */
73
+ #define FIX_0_765366865 ((int32)6270) /* FIX(0.765366865) */
74
+ #define FIX_0_899976223 ((int32)7373) /* FIX(0.899976223) */
75
+ #define FIX_1_175875602 ((int32)9633) /* FIX(1.175875602) */
76
+ #define FIX_1_501321110 ((int32)12299) /* FIX(1.501321110) */
77
+ #define FIX_1_847759065 ((int32)15137) /* FIX(1.847759065) */
78
+ #define FIX_1_961570560 ((int32)16069) /* FIX(1.961570560) */
79
+ #define FIX_2_053119869 ((int32)16819) /* FIX(2.053119869) */
80
+ #define FIX_2_562915447 ((int32)20995) /* FIX(2.562915447) */
81
+ #define FIX_3_072711026 ((int32)25172) /* FIX(3.072711026) */
82
+
83
+ #define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n))
84
+ #define DESCALE_ZEROSHIFT(x,n) (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n))
85
+
86
+ #define MULTIPLY(var, cnst) ((var) * (cnst))
87
+
88
+ #define CLAMP(i) ((static_cast<uint>(i) > 255) ? (((~i) >> 31) & 0xFF) : (i))
89
+
90
+ // Compiler creates a fast path 1D IDCT for X non-zero columns
91
+ template <int NONZERO_COLS>
92
+ struct Row
93
+ {
94
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
95
+ {
96
+ // ACCESS_COL() will be optimized at compile time to either an array access, or 0.
97
+ #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0)
98
+
99
+ const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6);
100
+
101
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
102
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
103
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
104
+
105
+ const int tmp0 = (ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS;
106
+ const int tmp1 = (ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS;
107
+
108
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
109
+
110
+ const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1);
111
+
112
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
113
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
114
+
115
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
116
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
117
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
118
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
119
+
120
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
121
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
122
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
123
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
124
+
125
+ pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS);
126
+ pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS);
127
+ pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS);
128
+ pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS);
129
+ pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS);
130
+ pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS);
131
+ pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS);
132
+ pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS);
133
+ }
134
+ };
135
+
136
+ template <>
137
+ struct Row<0>
138
+ {
139
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
140
+ {
141
+ #ifdef _MSC_VER
142
+ pTemp; pSrc;
143
+ #endif
144
+ }
145
+ };
146
+
147
+ template <>
148
+ struct Row<1>
149
+ {
150
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
151
+ {
152
+ const int dcval = (pSrc[0] << PASS1_BITS);
153
+
154
+ pTemp[0] = dcval;
155
+ pTemp[1] = dcval;
156
+ pTemp[2] = dcval;
157
+ pTemp[3] = dcval;
158
+ pTemp[4] = dcval;
159
+ pTemp[5] = dcval;
160
+ pTemp[6] = dcval;
161
+ pTemp[7] = dcval;
162
+ }
163
+ };
164
+
165
+ // Compiler creates a fast path 1D IDCT for X non-zero rows
166
+ template <int NONZERO_ROWS>
167
+ struct Col
168
+ {
169
+ static void idct(uint8* pDst_ptr, const int* pTemp)
170
+ {
171
+ // ACCESS_ROW() will be optimized at compile time to either an array access, or 0.
172
+ #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0)
173
+
174
+ const int z2 = ACCESS_ROW(2);
175
+ const int z3 = ACCESS_ROW(6);
176
+
177
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
178
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
179
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
180
+
181
+ const int tmp0 = (ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS;
182
+ const int tmp1 = (ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS;
183
+
184
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
185
+
186
+ const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1);
187
+
188
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
189
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
190
+
191
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
192
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
193
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
194
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
195
+
196
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
197
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
198
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
199
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
200
+
201
+ int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3);
202
+ pDst_ptr[8*0] = (uint8)CLAMP(i);
203
+
204
+ i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3);
205
+ pDst_ptr[8*7] = (uint8)CLAMP(i);
206
+
207
+ i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3);
208
+ pDst_ptr[8*1] = (uint8)CLAMP(i);
209
+
210
+ i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3);
211
+ pDst_ptr[8*6] = (uint8)CLAMP(i);
212
+
213
+ i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3);
214
+ pDst_ptr[8*2] = (uint8)CLAMP(i);
215
+
216
+ i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3);
217
+ pDst_ptr[8*5] = (uint8)CLAMP(i);
218
+
219
+ i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3);
220
+ pDst_ptr[8*3] = (uint8)CLAMP(i);
221
+
222
+ i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3);
223
+ pDst_ptr[8*4] = (uint8)CLAMP(i);
224
+ }
225
+ };
226
+
227
+ template <>
228
+ struct Col<1>
229
+ {
230
+ static void idct(uint8* pDst_ptr, const int* pTemp)
231
+ {
232
+ int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3);
233
+ const uint8 dcval_clamped = (uint8)CLAMP(dcval);
234
+ pDst_ptr[0*8] = dcval_clamped;
235
+ pDst_ptr[1*8] = dcval_clamped;
236
+ pDst_ptr[2*8] = dcval_clamped;
237
+ pDst_ptr[3*8] = dcval_clamped;
238
+ pDst_ptr[4*8] = dcval_clamped;
239
+ pDst_ptr[5*8] = dcval_clamped;
240
+ pDst_ptr[6*8] = dcval_clamped;
241
+ pDst_ptr[7*8] = dcval_clamped;
242
+ }
243
+ };
244
+
245
+ static const uint8 s_idct_row_table[] =
246
+ {
247
+ 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0,
248
+ 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0,
249
+ 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0,
250
+ 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0,
251
+ 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2,
252
+ 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2,
253
+ 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4,
254
+ 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8,
255
+ };
256
+
257
+ static const uint8 s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 };
258
+
259
+ void idct(const jpgd_block_t* pSrc_ptr, uint8* pDst_ptr, int block_max_zag)
260
+ {
261
+ JPGD_ASSERT(block_max_zag >= 1);
262
+ JPGD_ASSERT(block_max_zag <= 64);
263
+
264
+ if (block_max_zag == 1)
265
+ {
266
+ int k = ((pSrc_ptr[0] + 4) >> 3) + 128;
267
+ k = CLAMP(k);
268
+ k = k | (k<<8);
269
+ k = k | (k<<16);
270
+
271
+ for (int i = 8; i > 0; i--)
272
+ {
273
+ *(int*)&pDst_ptr[0] = k;
274
+ *(int*)&pDst_ptr[4] = k;
275
+ pDst_ptr += 8;
276
+ }
277
+ return;
278
+ }
279
+
280
+ int temp[64];
281
+
282
+ const jpgd_block_t* pSrc = pSrc_ptr;
283
+ int* pTemp = temp;
284
+
285
+ const uint8* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8];
286
+ int i;
287
+ for (i = 8; i > 0; i--, pRow_tab++)
288
+ {
289
+ switch (*pRow_tab)
290
+ {
291
+ case 0: Row<0>::idct(pTemp, pSrc); break;
292
+ case 1: Row<1>::idct(pTemp, pSrc); break;
293
+ case 2: Row<2>::idct(pTemp, pSrc); break;
294
+ case 3: Row<3>::idct(pTemp, pSrc); break;
295
+ case 4: Row<4>::idct(pTemp, pSrc); break;
296
+ case 5: Row<5>::idct(pTemp, pSrc); break;
297
+ case 6: Row<6>::idct(pTemp, pSrc); break;
298
+ case 7: Row<7>::idct(pTemp, pSrc); break;
299
+ case 8: Row<8>::idct(pTemp, pSrc); break;
300
+ }
301
+
302
+ pSrc += 8;
303
+ pTemp += 8;
304
+ }
305
+
306
+ pTemp = temp;
307
+
308
+ const int nonzero_rows = s_idct_col_table[block_max_zag - 1];
309
+ for (i = 8; i > 0; i--)
310
+ {
311
+ switch (nonzero_rows)
312
+ {
313
+ case 1: Col<1>::idct(pDst_ptr, pTemp); break;
314
+ case 2: Col<2>::idct(pDst_ptr, pTemp); break;
315
+ case 3: Col<3>::idct(pDst_ptr, pTemp); break;
316
+ case 4: Col<4>::idct(pDst_ptr, pTemp); break;
317
+ case 5: Col<5>::idct(pDst_ptr, pTemp); break;
318
+ case 6: Col<6>::idct(pDst_ptr, pTemp); break;
319
+ case 7: Col<7>::idct(pDst_ptr, pTemp); break;
320
+ case 8: Col<8>::idct(pDst_ptr, pTemp); break;
321
+ }
322
+
323
+ pTemp++;
324
+ pDst_ptr++;
325
+ }
326
+ }
327
+
328
+ void idct_4x4(const jpgd_block_t* pSrc_ptr, uint8* pDst_ptr)
329
+ {
330
+ int temp[64];
331
+ int* pTemp = temp;
332
+ const jpgd_block_t* pSrc = pSrc_ptr;
333
+
334
+ for (int i = 4; i > 0; i--)
335
+ {
336
+ Row<4>::idct(pTemp, pSrc);
337
+ pSrc += 8;
338
+ pTemp += 8;
339
+ }
340
+
341
+ pTemp = temp;
342
+ for (int i = 8; i > 0; i--)
343
+ {
344
+ Col<4>::idct(pDst_ptr, pTemp);
345
+ pTemp++;
346
+ pDst_ptr++;
347
+ }
348
+ }
349
+
350
+ // Retrieve one character from the input stream.
351
+ inline uint jpeg_decoder::get_char()
352
+ {
353
+ // Any bytes remaining in buffer?
354
+ if (!m_in_buf_left)
355
+ {
356
+ // Try to get more bytes.
357
+ prep_in_buffer();
358
+ // Still nothing to get?
359
+ if (!m_in_buf_left)
360
+ {
361
+ // Pad the end of the stream with 0xFF 0xD9 (EOI marker)
362
+ int t = m_tem_flag;
363
+ m_tem_flag ^= 1;
364
+ if (t)
365
+ return 0xD9;
366
+ else
367
+ return 0xFF;
368
+ }
369
+ }
370
+
371
+ uint c = *m_pIn_buf_ofs++;
372
+ m_in_buf_left--;
373
+
374
+ return c;
375
+ }
376
+
377
+ // Same as previous method, except can indicate if the character is a pad character or not.
378
+ inline uint jpeg_decoder::get_char(bool *pPadding_flag)
379
+ {
380
+ if (!m_in_buf_left)
381
+ {
382
+ prep_in_buffer();
383
+ if (!m_in_buf_left)
384
+ {
385
+ *pPadding_flag = true;
386
+ int t = m_tem_flag;
387
+ m_tem_flag ^= 1;
388
+ if (t)
389
+ return 0xD9;
390
+ else
391
+ return 0xFF;
392
+ }
393
+ }
394
+
395
+ *pPadding_flag = false;
396
+
397
+ uint c = *m_pIn_buf_ofs++;
398
+ m_in_buf_left--;
399
+
400
+ return c;
401
+ }
402
+
403
+ // Inserts a previously retrieved character back into the input buffer.
404
+ inline void jpeg_decoder::stuff_char(uint8 q)
405
+ {
406
+ *(--m_pIn_buf_ofs) = q;
407
+ m_in_buf_left++;
408
+ }
409
+
410
+ // Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered.
411
+ inline uint8 jpeg_decoder::get_octet()
412
+ {
413
+ bool padding_flag;
414
+ int c = get_char(&padding_flag);
415
+
416
+ if (c == 0xFF)
417
+ {
418
+ if (padding_flag)
419
+ return 0xFF;
420
+
421
+ c = get_char(&padding_flag);
422
+ if (padding_flag)
423
+ {
424
+ stuff_char(0xFF);
425
+ return 0xFF;
426
+ }
427
+
428
+ if (c == 0x00)
429
+ return 0xFF;
430
+ else
431
+ {
432
+ stuff_char(static_cast<uint8>(c));
433
+ stuff_char(0xFF);
434
+ return 0xFF;
435
+ }
436
+ }
437
+
438
+ return static_cast<uint8>(c);
439
+ }
440
+
441
+ // Retrieves a variable number of bits from the input stream. Does not recognize markers.
442
+ inline uint jpeg_decoder::get_bits(int num_bits)
443
+ {
444
+ if (!num_bits)
445
+ return 0;
446
+
447
+ uint i = m_bit_buf >> (32 - num_bits);
448
+
449
+ if ((m_bits_left -= num_bits) <= 0)
450
+ {
451
+ m_bit_buf <<= (num_bits += m_bits_left);
452
+
453
+ uint c1 = get_char();
454
+ uint c2 = get_char();
455
+ m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2;
456
+
457
+ m_bit_buf <<= -m_bits_left;
458
+
459
+ m_bits_left += 16;
460
+
461
+ JPGD_ASSERT(m_bits_left >= 0);
462
+ }
463
+ else
464
+ m_bit_buf <<= num_bits;
465
+
466
+ return i;
467
+ }
468
+
469
+ // Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered.
470
+ inline uint jpeg_decoder::get_bits_no_markers(int num_bits)
471
+ {
472
+ if (!num_bits)
473
+ return 0;
474
+
475
+ uint i = m_bit_buf >> (32 - num_bits);
476
+
477
+ if ((m_bits_left -= num_bits) <= 0)
478
+ {
479
+ m_bit_buf <<= (num_bits += m_bits_left);
480
+
481
+ if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF))
482
+ {
483
+ uint c1 = get_octet();
484
+ uint c2 = get_octet();
485
+ m_bit_buf |= (c1 << 8) | c2;
486
+ }
487
+ else
488
+ {
489
+ m_bit_buf |= ((uint)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1];
490
+ m_in_buf_left -= 2;
491
+ m_pIn_buf_ofs += 2;
492
+ }
493
+
494
+ m_bit_buf <<= -m_bits_left;
495
+
496
+ m_bits_left += 16;
497
+
498
+ JPGD_ASSERT(m_bits_left >= 0);
499
+ }
500
+ else
501
+ m_bit_buf <<= num_bits;
502
+
503
+ return i;
504
+ }
505
+
506
+ // Decodes a Huffman encoded symbol.
507
+ inline int jpeg_decoder::huff_decode(huff_tables *pH)
508
+ {
509
+ int symbol;
510
+
511
+ // Check first 8-bits: do we have a complete symbol?
512
+ if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0)
513
+ {
514
+ // Decode more bits, use a tree traversal to find symbol.
515
+ int ofs = 23;
516
+ do
517
+ {
518
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
519
+ ofs--;
520
+ } while (symbol < 0);
521
+
522
+ get_bits_no_markers(8 + (23 - ofs));
523
+ }
524
+ else
525
+ get_bits_no_markers(pH->code_size[symbol]);
526
+
527
+ return symbol;
528
+ }
529
+
530
+ // Decodes a Huffman encoded symbol.
531
+ inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
532
+ {
533
+ int symbol;
534
+
535
+ // Check first 8-bits: do we have a complete symbol?
536
+ if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0)
537
+ {
538
+ // Use a tree traversal to find symbol.
539
+ int ofs = 23;
540
+ do
541
+ {
542
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
543
+ ofs--;
544
+ } while (symbol < 0);
545
+
546
+ get_bits_no_markers(8 + (23 - ofs));
547
+
548
+ extra_bits = get_bits_no_markers(symbol & 0xF);
549
+ }
550
+ else
551
+ {
552
+ JPGD_ASSERT(((symbol >> 8) & 31) == pH->code_size[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0));
553
+
554
+ if (symbol & 0x8000)
555
+ {
556
+ get_bits_no_markers((symbol >> 8) & 31);
557
+ extra_bits = symbol >> 16;
558
+ }
559
+ else
560
+ {
561
+ int code_size = (symbol >> 8) & 31;
562
+ int num_extra_bits = symbol & 0xF;
563
+ int bits = code_size + num_extra_bits;
564
+ if (bits <= (m_bits_left + 16))
565
+ extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1);
566
+ else
567
+ {
568
+ get_bits_no_markers(code_size);
569
+ extra_bits = get_bits_no_markers(num_extra_bits);
570
+ }
571
+ }
572
+
573
+ symbol &= 0xFF;
574
+ }
575
+
576
+ return symbol;
577
+ }
578
+
579
+ // Tables and macro used to fully decode the DPCM differences.
580
+ static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
581
+ static const int s_extend_offset[16] = { 0, -1, -3, -7, -15, -31, -63, -127, -255, -511, -1023, -2047, -4095, -8191, -16383, -32767 };
582
+ static const int s_extend_mask[] = { 0, (1<<0), (1<<1), (1<<2), (1<<3), (1<<4), (1<<5), (1<<6), (1<<7), (1<<8), (1<<9), (1<<10), (1<<11), (1<<12), (1<<13), (1<<14), (1<<15), (1<<16) };
583
+ #define HUFF_EXTEND(x,s) ((x) < s_extend_test[s] ? (x) + s_extend_offset[s] : (x))
584
+
585
+ // Clamps a value between 0-255.
586
+ inline uint8 jpeg_decoder::clamp(int i)
587
+ {
588
+ if (static_cast<uint>(i) > 255)
589
+ i = (((~i) >> 31) & 0xFF);
590
+
591
+ return static_cast<uint8>(i);
592
+ }
593
+
594
+ namespace DCT_Upsample
595
+ {
596
+ struct Matrix44
597
+ {
598
+ typedef int Element_Type;
599
+ enum { NUM_ROWS = 4, NUM_COLS = 4 };
600
+
601
+ Element_Type v[NUM_ROWS][NUM_COLS];
602
+
603
+ inline int rows() const { return NUM_ROWS; }
604
+ inline int cols() const { return NUM_COLS; }
605
+
606
+ inline const Element_Type & at(int r, int c) const { return v[r][c]; }
607
+ inline Element_Type & at(int r, int c) { return v[r][c]; }
608
+
609
+ inline Matrix44() { }
610
+
611
+ inline Matrix44& operator += (const Matrix44& a)
612
+ {
613
+ for (int r = 0; r < NUM_ROWS; r++)
614
+ {
615
+ at(r, 0) += a.at(r, 0);
616
+ at(r, 1) += a.at(r, 1);
617
+ at(r, 2) += a.at(r, 2);
618
+ at(r, 3) += a.at(r, 3);
619
+ }
620
+ return *this;
621
+ }
622
+
623
+ inline Matrix44& operator -= (const Matrix44& a)
624
+ {
625
+ for (int r = 0; r < NUM_ROWS; r++)
626
+ {
627
+ at(r, 0) -= a.at(r, 0);
628
+ at(r, 1) -= a.at(r, 1);
629
+ at(r, 2) -= a.at(r, 2);
630
+ at(r, 3) -= a.at(r, 3);
631
+ }
632
+ return *this;
633
+ }
634
+
635
+ friend inline Matrix44 operator + (const Matrix44& a, const Matrix44& b)
636
+ {
637
+ Matrix44 ret;
638
+ for (int r = 0; r < NUM_ROWS; r++)
639
+ {
640
+ ret.at(r, 0) = a.at(r, 0) + b.at(r, 0);
641
+ ret.at(r, 1) = a.at(r, 1) + b.at(r, 1);
642
+ ret.at(r, 2) = a.at(r, 2) + b.at(r, 2);
643
+ ret.at(r, 3) = a.at(r, 3) + b.at(r, 3);
644
+ }
645
+ return ret;
646
+ }
647
+
648
+ friend inline Matrix44 operator - (const Matrix44& a, const Matrix44& b)
649
+ {
650
+ Matrix44 ret;
651
+ for (int r = 0; r < NUM_ROWS; r++)
652
+ {
653
+ ret.at(r, 0) = a.at(r, 0) - b.at(r, 0);
654
+ ret.at(r, 1) = a.at(r, 1) - b.at(r, 1);
655
+ ret.at(r, 2) = a.at(r, 2) - b.at(r, 2);
656
+ ret.at(r, 3) = a.at(r, 3) - b.at(r, 3);
657
+ }
658
+ return ret;
659
+ }
660
+
661
+ static inline void add_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
662
+ {
663
+ for (int r = 0; r < 4; r++)
664
+ {
665
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) + b.at(r, 0));
666
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) + b.at(r, 1));
667
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) + b.at(r, 2));
668
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) + b.at(r, 3));
669
+ }
670
+ }
671
+
672
+ static inline void sub_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
673
+ {
674
+ for (int r = 0; r < 4; r++)
675
+ {
676
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) - b.at(r, 0));
677
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) - b.at(r, 1));
678
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) - b.at(r, 2));
679
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) - b.at(r, 3));
680
+ }
681
+ }
682
+ };
683
+
684
+ const int FRACT_BITS = 10;
685
+ const int SCALE = 1 << FRACT_BITS;
686
+
687
+ typedef int Temp_Type;
688
+ #define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS)
689
+ #define F(i) ((int)((i) * SCALE + .5f))
690
+
691
+ // Any decent C++ compiler will optimize this at compile time to a 0, or an array access.
692
+ #define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8])
693
+
694
+ // NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix
695
+ template<int NUM_ROWS, int NUM_COLS>
696
+ struct P_Q
697
+ {
698
+ static void calc(Matrix44& P, Matrix44& Q, const jpgd_block_t* pSrc)
699
+ {
700
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
701
+ const Temp_Type X000 = AT(0, 0);
702
+ const Temp_Type X001 = AT(0, 1);
703
+ const Temp_Type X002 = AT(0, 2);
704
+ const Temp_Type X003 = AT(0, 3);
705
+ const Temp_Type X004 = AT(0, 4);
706
+ const Temp_Type X005 = AT(0, 5);
707
+ const Temp_Type X006 = AT(0, 6);
708
+ const Temp_Type X007 = AT(0, 7);
709
+ const Temp_Type X010 = D(F(0.415735f) * AT(1, 0) + F(0.791065f) * AT(3, 0) + F(-0.352443f) * AT(5, 0) + F(0.277785f) * AT(7, 0));
710
+ const Temp_Type X011 = D(F(0.415735f) * AT(1, 1) + F(0.791065f) * AT(3, 1) + F(-0.352443f) * AT(5, 1) + F(0.277785f) * AT(7, 1));
711
+ const Temp_Type X012 = D(F(0.415735f) * AT(1, 2) + F(0.791065f) * AT(3, 2) + F(-0.352443f) * AT(5, 2) + F(0.277785f) * AT(7, 2));
712
+ const Temp_Type X013 = D(F(0.415735f) * AT(1, 3) + F(0.791065f) * AT(3, 3) + F(-0.352443f) * AT(5, 3) + F(0.277785f) * AT(7, 3));
713
+ const Temp_Type X014 = D(F(0.415735f) * AT(1, 4) + F(0.791065f) * AT(3, 4) + F(-0.352443f) * AT(5, 4) + F(0.277785f) * AT(7, 4));
714
+ const Temp_Type X015 = D(F(0.415735f) * AT(1, 5) + F(0.791065f) * AT(3, 5) + F(-0.352443f) * AT(5, 5) + F(0.277785f) * AT(7, 5));
715
+ const Temp_Type X016 = D(F(0.415735f) * AT(1, 6) + F(0.791065f) * AT(3, 6) + F(-0.352443f) * AT(5, 6) + F(0.277785f) * AT(7, 6));
716
+ const Temp_Type X017 = D(F(0.415735f) * AT(1, 7) + F(0.791065f) * AT(3, 7) + F(-0.352443f) * AT(5, 7) + F(0.277785f) * AT(7, 7));
717
+ const Temp_Type X020 = AT(4, 0);
718
+ const Temp_Type X021 = AT(4, 1);
719
+ const Temp_Type X022 = AT(4, 2);
720
+ const Temp_Type X023 = AT(4, 3);
721
+ const Temp_Type X024 = AT(4, 4);
722
+ const Temp_Type X025 = AT(4, 5);
723
+ const Temp_Type X026 = AT(4, 6);
724
+ const Temp_Type X027 = AT(4, 7);
725
+ const Temp_Type X030 = D(F(0.022887f) * AT(1, 0) + F(-0.097545f) * AT(3, 0) + F(0.490393f) * AT(5, 0) + F(0.865723f) * AT(7, 0));
726
+ const Temp_Type X031 = D(F(0.022887f) * AT(1, 1) + F(-0.097545f) * AT(3, 1) + F(0.490393f) * AT(5, 1) + F(0.865723f) * AT(7, 1));
727
+ const Temp_Type X032 = D(F(0.022887f) * AT(1, 2) + F(-0.097545f) * AT(3, 2) + F(0.490393f) * AT(5, 2) + F(0.865723f) * AT(7, 2));
728
+ const Temp_Type X033 = D(F(0.022887f) * AT(1, 3) + F(-0.097545f) * AT(3, 3) + F(0.490393f) * AT(5, 3) + F(0.865723f) * AT(7, 3));
729
+ const Temp_Type X034 = D(F(0.022887f) * AT(1, 4) + F(-0.097545f) * AT(3, 4) + F(0.490393f) * AT(5, 4) + F(0.865723f) * AT(7, 4));
730
+ const Temp_Type X035 = D(F(0.022887f) * AT(1, 5) + F(-0.097545f) * AT(3, 5) + F(0.490393f) * AT(5, 5) + F(0.865723f) * AT(7, 5));
731
+ const Temp_Type X036 = D(F(0.022887f) * AT(1, 6) + F(-0.097545f) * AT(3, 6) + F(0.490393f) * AT(5, 6) + F(0.865723f) * AT(7, 6));
732
+ const Temp_Type X037 = D(F(0.022887f) * AT(1, 7) + F(-0.097545f) * AT(3, 7) + F(0.490393f) * AT(5, 7) + F(0.865723f) * AT(7, 7));
733
+
734
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
735
+ P.at(0, 0) = X000;
736
+ P.at(0, 1) = D(X001 * F(0.415735f) + X003 * F(0.791065f) + X005 * F(-0.352443f) + X007 * F(0.277785f));
737
+ P.at(0, 2) = X004;
738
+ P.at(0, 3) = D(X001 * F(0.022887f) + X003 * F(-0.097545f) + X005 * F(0.490393f) + X007 * F(0.865723f));
739
+ P.at(1, 0) = X010;
740
+ P.at(1, 1) = D(X011 * F(0.415735f) + X013 * F(0.791065f) + X015 * F(-0.352443f) + X017 * F(0.277785f));
741
+ P.at(1, 2) = X014;
742
+ P.at(1, 3) = D(X011 * F(0.022887f) + X013 * F(-0.097545f) + X015 * F(0.490393f) + X017 * F(0.865723f));
743
+ P.at(2, 0) = X020;
744
+ P.at(2, 1) = D(X021 * F(0.415735f) + X023 * F(0.791065f) + X025 * F(-0.352443f) + X027 * F(0.277785f));
745
+ P.at(2, 2) = X024;
746
+ P.at(2, 3) = D(X021 * F(0.022887f) + X023 * F(-0.097545f) + X025 * F(0.490393f) + X027 * F(0.865723f));
747
+ P.at(3, 0) = X030;
748
+ P.at(3, 1) = D(X031 * F(0.415735f) + X033 * F(0.791065f) + X035 * F(-0.352443f) + X037 * F(0.277785f));
749
+ P.at(3, 2) = X034;
750
+ P.at(3, 3) = D(X031 * F(0.022887f) + X033 * F(-0.097545f) + X035 * F(0.490393f) + X037 * F(0.865723f));
751
+ // 40 muls 24 adds
752
+
753
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
754
+ Q.at(0, 0) = D(X001 * F(0.906127f) + X003 * F(-0.318190f) + X005 * F(0.212608f) + X007 * F(-0.180240f));
755
+ Q.at(0, 1) = X002;
756
+ Q.at(0, 2) = D(X001 * F(-0.074658f) + X003 * F(0.513280f) + X005 * F(0.768178f) + X007 * F(-0.375330f));
757
+ Q.at(0, 3) = X006;
758
+ Q.at(1, 0) = D(X011 * F(0.906127f) + X013 * F(-0.318190f) + X015 * F(0.212608f) + X017 * F(-0.180240f));
759
+ Q.at(1, 1) = X012;
760
+ Q.at(1, 2) = D(X011 * F(-0.074658f) + X013 * F(0.513280f) + X015 * F(0.768178f) + X017 * F(-0.375330f));
761
+ Q.at(1, 3) = X016;
762
+ Q.at(2, 0) = D(X021 * F(0.906127f) + X023 * F(-0.318190f) + X025 * F(0.212608f) + X027 * F(-0.180240f));
763
+ Q.at(2, 1) = X022;
764
+ Q.at(2, 2) = D(X021 * F(-0.074658f) + X023 * F(0.513280f) + X025 * F(0.768178f) + X027 * F(-0.375330f));
765
+ Q.at(2, 3) = X026;
766
+ Q.at(3, 0) = D(X031 * F(0.906127f) + X033 * F(-0.318190f) + X035 * F(0.212608f) + X037 * F(-0.180240f));
767
+ Q.at(3, 1) = X032;
768
+ Q.at(3, 2) = D(X031 * F(-0.074658f) + X033 * F(0.513280f) + X035 * F(0.768178f) + X037 * F(-0.375330f));
769
+ Q.at(3, 3) = X036;
770
+ // 40 muls 24 adds
771
+ }
772
+ };
773
+
774
+ template<int NUM_ROWS, int NUM_COLS>
775
+ struct R_S
776
+ {
777
+ static void calc(Matrix44& R, Matrix44& S, const jpgd_block_t* pSrc)
778
+ {
779
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
780
+ const Temp_Type X100 = D(F(0.906127f) * AT(1, 0) + F(-0.318190f) * AT(3, 0) + F(0.212608f) * AT(5, 0) + F(-0.180240f) * AT(7, 0));
781
+ const Temp_Type X101 = D(F(0.906127f) * AT(1, 1) + F(-0.318190f) * AT(3, 1) + F(0.212608f) * AT(5, 1) + F(-0.180240f) * AT(7, 1));
782
+ const Temp_Type X102 = D(F(0.906127f) * AT(1, 2) + F(-0.318190f) * AT(3, 2) + F(0.212608f) * AT(5, 2) + F(-0.180240f) * AT(7, 2));
783
+ const Temp_Type X103 = D(F(0.906127f) * AT(1, 3) + F(-0.318190f) * AT(3, 3) + F(0.212608f) * AT(5, 3) + F(-0.180240f) * AT(7, 3));
784
+ const Temp_Type X104 = D(F(0.906127f) * AT(1, 4) + F(-0.318190f) * AT(3, 4) + F(0.212608f) * AT(5, 4) + F(-0.180240f) * AT(7, 4));
785
+ const Temp_Type X105 = D(F(0.906127f) * AT(1, 5) + F(-0.318190f) * AT(3, 5) + F(0.212608f) * AT(5, 5) + F(-0.180240f) * AT(7, 5));
786
+ const Temp_Type X106 = D(F(0.906127f) * AT(1, 6) + F(-0.318190f) * AT(3, 6) + F(0.212608f) * AT(5, 6) + F(-0.180240f) * AT(7, 6));
787
+ const Temp_Type X107 = D(F(0.906127f) * AT(1, 7) + F(-0.318190f) * AT(3, 7) + F(0.212608f) * AT(5, 7) + F(-0.180240f) * AT(7, 7));
788
+ const Temp_Type X110 = AT(2, 0);
789
+ const Temp_Type X111 = AT(2, 1);
790
+ const Temp_Type X112 = AT(2, 2);
791
+ const Temp_Type X113 = AT(2, 3);
792
+ const Temp_Type X114 = AT(2, 4);
793
+ const Temp_Type X115 = AT(2, 5);
794
+ const Temp_Type X116 = AT(2, 6);
795
+ const Temp_Type X117 = AT(2, 7);
796
+ const Temp_Type X120 = D(F(-0.074658f) * AT(1, 0) + F(0.513280f) * AT(3, 0) + F(0.768178f) * AT(5, 0) + F(-0.375330f) * AT(7, 0));
797
+ const Temp_Type X121 = D(F(-0.074658f) * AT(1, 1) + F(0.513280f) * AT(3, 1) + F(0.768178f) * AT(5, 1) + F(-0.375330f) * AT(7, 1));
798
+ const Temp_Type X122 = D(F(-0.074658f) * AT(1, 2) + F(0.513280f) * AT(3, 2) + F(0.768178f) * AT(5, 2) + F(-0.375330f) * AT(7, 2));
799
+ const Temp_Type X123 = D(F(-0.074658f) * AT(1, 3) + F(0.513280f) * AT(3, 3) + F(0.768178f) * AT(5, 3) + F(-0.375330f) * AT(7, 3));
800
+ const Temp_Type X124 = D(F(-0.074658f) * AT(1, 4) + F(0.513280f) * AT(3, 4) + F(0.768178f) * AT(5, 4) + F(-0.375330f) * AT(7, 4));
801
+ const Temp_Type X125 = D(F(-0.074658f) * AT(1, 5) + F(0.513280f) * AT(3, 5) + F(0.768178f) * AT(5, 5) + F(-0.375330f) * AT(7, 5));
802
+ const Temp_Type X126 = D(F(-0.074658f) * AT(1, 6) + F(0.513280f) * AT(3, 6) + F(0.768178f) * AT(5, 6) + F(-0.375330f) * AT(7, 6));
803
+ const Temp_Type X127 = D(F(-0.074658f) * AT(1, 7) + F(0.513280f) * AT(3, 7) + F(0.768178f) * AT(5, 7) + F(-0.375330f) * AT(7, 7));
804
+ const Temp_Type X130 = AT(6, 0);
805
+ const Temp_Type X131 = AT(6, 1);
806
+ const Temp_Type X132 = AT(6, 2);
807
+ const Temp_Type X133 = AT(6, 3);
808
+ const Temp_Type X134 = AT(6, 4);
809
+ const Temp_Type X135 = AT(6, 5);
810
+ const Temp_Type X136 = AT(6, 6);
811
+ const Temp_Type X137 = AT(6, 7);
812
+ // 80 muls 48 adds
813
+
814
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
815
+ R.at(0, 0) = X100;
816
+ R.at(0, 1) = D(X101 * F(0.415735f) + X103 * F(0.791065f) + X105 * F(-0.352443f) + X107 * F(0.277785f));
817
+ R.at(0, 2) = X104;
818
+ R.at(0, 3) = D(X101 * F(0.022887f) + X103 * F(-0.097545f) + X105 * F(0.490393f) + X107 * F(0.865723f));
819
+ R.at(1, 0) = X110;
820
+ R.at(1, 1) = D(X111 * F(0.415735f) + X113 * F(0.791065f) + X115 * F(-0.352443f) + X117 * F(0.277785f));
821
+ R.at(1, 2) = X114;
822
+ R.at(1, 3) = D(X111 * F(0.022887f) + X113 * F(-0.097545f) + X115 * F(0.490393f) + X117 * F(0.865723f));
823
+ R.at(2, 0) = X120;
824
+ R.at(2, 1) = D(X121 * F(0.415735f) + X123 * F(0.791065f) + X125 * F(-0.352443f) + X127 * F(0.277785f));
825
+ R.at(2, 2) = X124;
826
+ R.at(2, 3) = D(X121 * F(0.022887f) + X123 * F(-0.097545f) + X125 * F(0.490393f) + X127 * F(0.865723f));
827
+ R.at(3, 0) = X130;
828
+ R.at(3, 1) = D(X131 * F(0.415735f) + X133 * F(0.791065f) + X135 * F(-0.352443f) + X137 * F(0.277785f));
829
+ R.at(3, 2) = X134;
830
+ R.at(3, 3) = D(X131 * F(0.022887f) + X133 * F(-0.097545f) + X135 * F(0.490393f) + X137 * F(0.865723f));
831
+ // 40 muls 24 adds
832
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
833
+ S.at(0, 0) = D(X101 * F(0.906127f) + X103 * F(-0.318190f) + X105 * F(0.212608f) + X107 * F(-0.180240f));
834
+ S.at(0, 1) = X102;
835
+ S.at(0, 2) = D(X101 * F(-0.074658f) + X103 * F(0.513280f) + X105 * F(0.768178f) + X107 * F(-0.375330f));
836
+ S.at(0, 3) = X106;
837
+ S.at(1, 0) = D(X111 * F(0.906127f) + X113 * F(-0.318190f) + X115 * F(0.212608f) + X117 * F(-0.180240f));
838
+ S.at(1, 1) = X112;
839
+ S.at(1, 2) = D(X111 * F(-0.074658f) + X113 * F(0.513280f) + X115 * F(0.768178f) + X117 * F(-0.375330f));
840
+ S.at(1, 3) = X116;
841
+ S.at(2, 0) = D(X121 * F(0.906127f) + X123 * F(-0.318190f) + X125 * F(0.212608f) + X127 * F(-0.180240f));
842
+ S.at(2, 1) = X122;
843
+ S.at(2, 2) = D(X121 * F(-0.074658f) + X123 * F(0.513280f) + X125 * F(0.768178f) + X127 * F(-0.375330f));
844
+ S.at(2, 3) = X126;
845
+ S.at(3, 0) = D(X131 * F(0.906127f) + X133 * F(-0.318190f) + X135 * F(0.212608f) + X137 * F(-0.180240f));
846
+ S.at(3, 1) = X132;
847
+ S.at(3, 2) = D(X131 * F(-0.074658f) + X133 * F(0.513280f) + X135 * F(0.768178f) + X137 * F(-0.375330f));
848
+ S.at(3, 3) = X136;
849
+ // 40 muls 24 adds
850
+ }
851
+ };
852
+ } // end namespace DCT_Upsample
853
+
854
+ // Unconditionally frees all allocated m_blocks.
855
+ void jpeg_decoder::free_all_blocks()
856
+ {
857
+ m_pStream = NULL;
858
+ for (mem_block *b = m_pMem_blocks; b; )
859
+ {
860
+ mem_block *n = b->m_pNext;
861
+ jpgd_free(b);
862
+ b = n;
863
+ }
864
+ m_pMem_blocks = NULL;
865
+ }
866
+
867
+ // This method handles all errors.
868
+ // It could easily be changed to use C++ exceptions.
869
+ void jpeg_decoder::stop_decoding(jpgd_status status)
870
+ {
871
+ m_error_code = status;
872
+ free_all_blocks();
873
+ longjmp(m_jmp_state, status);
874
+
875
+ // we shouldn't get here as longjmp shouldn't return, but we put it here to make it explicit
876
+ // that this function doesn't return, otherwise we get this error:
877
+ //
878
+ // error : function declared 'noreturn' should not return
879
+ exit(1);
880
+ }
881
+
882
+ void *jpeg_decoder::alloc(size_t nSize, bool zero)
883
+ {
884
+ nSize = (JPGD_MAX(nSize, 1) + 3) & ~3;
885
+ char *rv = NULL;
886
+ for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext)
887
+ {
888
+ if ((b->m_used_count + nSize) <= b->m_size)
889
+ {
890
+ rv = b->m_data + b->m_used_count;
891
+ b->m_used_count += nSize;
892
+ break;
893
+ }
894
+ }
895
+ if (!rv)
896
+ {
897
+ int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
898
+ mem_block *b = (mem_block*)jpgd_malloc(sizeof(mem_block) + capacity);
899
+ if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
900
+ b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
901
+ b->m_used_count = nSize;
902
+ b->m_size = capacity;
903
+ rv = b->m_data;
904
+ }
905
+ if (zero) memset(rv, 0, nSize);
906
+ return rv;
907
+ }
908
+
909
+ void jpeg_decoder::word_clear(void *p, uint16 c, uint n)
910
+ {
911
+ uint8 *pD = (uint8*)p;
912
+ const uint8 l = c & 0xFF, h = (c >> 8) & 0xFF;
913
+ while (n)
914
+ {
915
+ pD[0] = l; pD[1] = h; pD += 2;
916
+ n--;
917
+ }
918
+ }
919
+
920
+ // Refill the input buffer.
921
+ // This method will sit in a loop until (A) the buffer is full or (B)
922
+ // the stream's read() method reports and end of file condition.
923
+ void jpeg_decoder::prep_in_buffer()
924
+ {
925
+ m_in_buf_left = 0;
926
+ m_pIn_buf_ofs = m_in_buf;
927
+
928
+ if (m_eof_flag)
929
+ return;
930
+
931
+ do
932
+ {
933
+ int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
934
+ if (bytes_read == -1)
935
+ stop_decoding(JPGD_STREAM_READ);
936
+
937
+ m_in_buf_left += bytes_read;
938
+ } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
939
+
940
+ m_total_bytes_read += m_in_buf_left;
941
+
942
+ // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
943
+ // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
944
+ word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
945
+ }
946
+
947
+ // Read a Huffman code table.
948
+ void jpeg_decoder::read_dht_marker()
949
+ {
950
+ int i, index, count;
951
+ uint8 huff_num[17];
952
+ uint8 huff_val[256];
953
+
954
+ uint num_left = get_bits(16);
955
+
956
+ if (num_left < 2)
957
+ stop_decoding(JPGD_BAD_DHT_MARKER);
958
+
959
+ num_left -= 2;
960
+
961
+ while (num_left)
962
+ {
963
+ index = get_bits(8);
964
+
965
+ huff_num[0] = 0;
966
+
967
+ count = 0;
968
+
969
+ for (i = 1; i <= 16; i++)
970
+ {
971
+ huff_num[i] = static_cast<uint8>(get_bits(8));
972
+ count += huff_num[i];
973
+ }
974
+
975
+ if (count > 255)
976
+ stop_decoding(JPGD_BAD_DHT_COUNTS);
977
+
978
+ for (i = 0; i < count; i++)
979
+ huff_val[i] = static_cast<uint8>(get_bits(8));
980
+
981
+ i = 1 + 16 + count;
982
+
983
+ if (num_left < (uint)i)
984
+ stop_decoding(JPGD_BAD_DHT_MARKER);
985
+
986
+ num_left -= i;
987
+
988
+ if ((index & 0x10) > 0x10)
989
+ stop_decoding(JPGD_BAD_DHT_INDEX);
990
+
991
+ index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
992
+
993
+ if (index >= JPGD_MAX_HUFF_TABLES)
994
+ stop_decoding(JPGD_BAD_DHT_INDEX);
995
+
996
+ if (!m_huff_num[index])
997
+ m_huff_num[index] = (uint8 *)alloc(17);
998
+
999
+ if (!m_huff_val[index])
1000
+ m_huff_val[index] = (uint8 *)alloc(256);
1001
+
1002
+ m_huff_ac[index] = (index & 0x10) != 0;
1003
+ memcpy(m_huff_num[index], huff_num, 17);
1004
+ memcpy(m_huff_val[index], huff_val, 256);
1005
+ }
1006
+ }
1007
+
1008
+ // Read a quantization table.
1009
+ void jpeg_decoder::read_dqt_marker()
1010
+ {
1011
+ int n, i, prec;
1012
+ uint num_left;
1013
+ uint temp;
1014
+
1015
+ num_left = get_bits(16);
1016
+
1017
+ if (num_left < 2)
1018
+ stop_decoding(JPGD_BAD_DQT_MARKER);
1019
+
1020
+ num_left -= 2;
1021
+
1022
+ while (num_left)
1023
+ {
1024
+ n = get_bits(8);
1025
+ prec = n >> 4;
1026
+ n &= 0x0F;
1027
+
1028
+ if (n >= JPGD_MAX_QUANT_TABLES)
1029
+ stop_decoding(JPGD_BAD_DQT_TABLE);
1030
+
1031
+ if (!m_quant[n])
1032
+ m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t));
1033
+
1034
+ // read quantization entries, in zag order
1035
+ for (i = 0; i < 64; i++)
1036
+ {
1037
+ temp = get_bits(8);
1038
+
1039
+ if (prec)
1040
+ temp = (temp << 8) + get_bits(8);
1041
+
1042
+ m_quant[n][i] = static_cast<jpgd_quant_t>(temp);
1043
+ }
1044
+
1045
+ i = 64 + 1;
1046
+
1047
+ if (prec)
1048
+ i += 64;
1049
+
1050
+ if (num_left < (uint)i)
1051
+ stop_decoding(JPGD_BAD_DQT_LENGTH);
1052
+
1053
+ num_left -= i;
1054
+ }
1055
+ }
1056
+
1057
+ // Read the start of frame (SOF) marker.
1058
+ void jpeg_decoder::read_sof_marker()
1059
+ {
1060
+ int i;
1061
+ uint num_left;
1062
+
1063
+ num_left = get_bits(16);
1064
+
1065
+ if (get_bits(8) != 8) /* precision: sorry, only 8-bit precision is supported right now */
1066
+ stop_decoding(JPGD_BAD_PRECISION);
1067
+
1068
+ m_image_y_size = get_bits(16);
1069
+
1070
+ if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT))
1071
+ stop_decoding(JPGD_BAD_HEIGHT);
1072
+
1073
+ m_image_x_size = get_bits(16);
1074
+
1075
+ if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH))
1076
+ stop_decoding(JPGD_BAD_WIDTH);
1077
+
1078
+ m_comps_in_frame = get_bits(8);
1079
+
1080
+ if (m_comps_in_frame > JPGD_MAX_COMPONENTS)
1081
+ stop_decoding(JPGD_TOO_MANY_COMPONENTS);
1082
+
1083
+ if (num_left != (uint)(m_comps_in_frame * 3 + 8))
1084
+ stop_decoding(JPGD_BAD_SOF_LENGTH);
1085
+
1086
+ for (i = 0; i < m_comps_in_frame; i++)
1087
+ {
1088
+ m_comp_ident[i] = get_bits(8);
1089
+ m_comp_h_samp[i] = get_bits(4);
1090
+ m_comp_v_samp[i] = get_bits(4);
1091
+ m_comp_quant[i] = get_bits(8);
1092
+ }
1093
+ }
1094
+
1095
+ // Used to skip unrecognized markers.
1096
+ void jpeg_decoder::skip_variable_marker()
1097
+ {
1098
+ uint num_left;
1099
+
1100
+ num_left = get_bits(16);
1101
+
1102
+ if (num_left < 2)
1103
+ stop_decoding(JPGD_BAD_VARIABLE_MARKER);
1104
+
1105
+ num_left -= 2;
1106
+
1107
+ while (num_left)
1108
+ {
1109
+ get_bits(8);
1110
+ num_left--;
1111
+ }
1112
+ }
1113
+
1114
+ // Read a define restart interval (DRI) marker.
1115
+ void jpeg_decoder::read_dri_marker()
1116
+ {
1117
+ if (get_bits(16) != 4)
1118
+ stop_decoding(JPGD_BAD_DRI_LENGTH);
1119
+
1120
+ m_restart_interval = get_bits(16);
1121
+ }
1122
+
1123
+ // Read a start of scan (SOS) marker.
1124
+ void jpeg_decoder::read_sos_marker()
1125
+ {
1126
+ uint num_left;
1127
+ int i, ci, n, c, cc;
1128
+
1129
+ num_left = get_bits(16);
1130
+
1131
+ n = get_bits(8);
1132
+
1133
+ m_comps_in_scan = n;
1134
+
1135
+ num_left -= 3;
1136
+
1137
+ if ( (num_left != (uint)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) )
1138
+ stop_decoding(JPGD_BAD_SOS_LENGTH);
1139
+
1140
+ for (i = 0; i < n; i++)
1141
+ {
1142
+ cc = get_bits(8);
1143
+ c = get_bits(8);
1144
+ num_left -= 2;
1145
+
1146
+ for (ci = 0; ci < m_comps_in_frame; ci++)
1147
+ if (cc == m_comp_ident[ci])
1148
+ break;
1149
+
1150
+ if (ci >= m_comps_in_frame)
1151
+ stop_decoding(JPGD_BAD_SOS_COMP_ID);
1152
+
1153
+ m_comp_list[i] = ci;
1154
+ m_comp_dc_tab[ci] = (c >> 4) & 15;
1155
+ m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1);
1156
+ }
1157
+
1158
+ m_spectral_start = get_bits(8);
1159
+ m_spectral_end = get_bits(8);
1160
+ m_successive_high = get_bits(4);
1161
+ m_successive_low = get_bits(4);
1162
+
1163
+ if (!m_progressive_flag)
1164
+ {
1165
+ m_spectral_start = 0;
1166
+ m_spectral_end = 63;
1167
+ }
1168
+
1169
+ num_left -= 3;
1170
+
1171
+ while (num_left) /* read past whatever is num_left */
1172
+ {
1173
+ get_bits(8);
1174
+ num_left--;
1175
+ }
1176
+ }
1177
+
1178
+ // Finds the next marker.
1179
+ int jpeg_decoder::next_marker()
1180
+ {
1181
+ uint c, bytes;
1182
+
1183
+ bytes = 0;
1184
+
1185
+ do
1186
+ {
1187
+ do
1188
+ {
1189
+ bytes++;
1190
+ c = get_bits(8);
1191
+ } while (c != 0xFF);
1192
+
1193
+ do
1194
+ {
1195
+ c = get_bits(8);
1196
+ } while (c == 0xFF);
1197
+
1198
+ } while (c == 0);
1199
+
1200
+ // If bytes > 0 here, there where extra bytes before the marker (not good).
1201
+
1202
+ return c;
1203
+ }
1204
+
1205
+ // Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
1206
+ // encountered.
1207
+ int jpeg_decoder::process_markers()
1208
+ {
1209
+ int c;
1210
+
1211
+ for ( ; ; )
1212
+ {
1213
+ c = next_marker();
1214
+
1215
+ switch (c)
1216
+ {
1217
+ case M_SOF0:
1218
+ case M_SOF1:
1219
+ case M_SOF2:
1220
+ case M_SOF3:
1221
+ case M_SOF5:
1222
+ case M_SOF6:
1223
+ case M_SOF7:
1224
+ // case M_JPG:
1225
+ case M_SOF9:
1226
+ case M_SOF10:
1227
+ case M_SOF11:
1228
+ case M_SOF13:
1229
+ case M_SOF14:
1230
+ case M_SOF15:
1231
+ case M_SOI:
1232
+ case M_EOI:
1233
+ case M_SOS:
1234
+ {
1235
+ return c;
1236
+ }
1237
+ case M_DHT:
1238
+ {
1239
+ read_dht_marker();
1240
+ break;
1241
+ }
1242
+ // No arithmitic support - dumb patents!
1243
+ case M_DAC:
1244
+ {
1245
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
1246
+ break;
1247
+ }
1248
+ case M_DQT:
1249
+ {
1250
+ read_dqt_marker();
1251
+ break;
1252
+ }
1253
+ case M_DRI:
1254
+ {
1255
+ read_dri_marker();
1256
+ break;
1257
+ }
1258
+ //case M_APP0: /* no need to read the JFIF marker */
1259
+
1260
+ case M_JPG:
1261
+ case M_RST0: /* no parameters */
1262
+ case M_RST1:
1263
+ case M_RST2:
1264
+ case M_RST3:
1265
+ case M_RST4:
1266
+ case M_RST5:
1267
+ case M_RST6:
1268
+ case M_RST7:
1269
+ case M_TEM:
1270
+ {
1271
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
1272
+ break;
1273
+ }
1274
+ default: /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
1275
+ {
1276
+ skip_variable_marker();
1277
+ break;
1278
+ }
1279
+ }
1280
+ }
1281
+ }
1282
+
1283
+ // Finds the start of image (SOI) marker.
1284
+ // This code is rather defensive: it only checks the first 512 bytes to avoid
1285
+ // false positives.
1286
+ void jpeg_decoder::locate_soi_marker()
1287
+ {
1288
+ uint lastchar, thischar;
1289
+ uint bytesleft;
1290
+
1291
+ lastchar = get_bits(8);
1292
+
1293
+ thischar = get_bits(8);
1294
+
1295
+ /* ok if it's a normal JPEG file without a special header */
1296
+
1297
+ if ((lastchar == 0xFF) && (thischar == M_SOI))
1298
+ return;
1299
+
1300
+ bytesleft = 4096; //512;
1301
+
1302
+ for ( ; ; )
1303
+ {
1304
+ if (--bytesleft == 0)
1305
+ stop_decoding(JPGD_NOT_JPEG);
1306
+
1307
+ lastchar = thischar;
1308
+
1309
+ thischar = get_bits(8);
1310
+
1311
+ if (lastchar == 0xFF)
1312
+ {
1313
+ if (thischar == M_SOI)
1314
+ break;
1315
+ else if (thischar == M_EOI) // get_bits will keep returning M_EOI if we read past the end
1316
+ stop_decoding(JPGD_NOT_JPEG);
1317
+ }
1318
+ }
1319
+
1320
+ // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
1321
+ thischar = (m_bit_buf >> 24) & 0xFF;
1322
+
1323
+ if (thischar != 0xFF)
1324
+ stop_decoding(JPGD_NOT_JPEG);
1325
+ }
1326
+
1327
+ // Find a start of frame (SOF) marker.
1328
+ void jpeg_decoder::locate_sof_marker()
1329
+ {
1330
+ locate_soi_marker();
1331
+
1332
+ int c = process_markers();
1333
+
1334
+ switch (c)
1335
+ {
1336
+ case M_SOF2:
1337
+ m_progressive_flag = JPGD_TRUE;
1338
+ case M_SOF0: /* baseline DCT */
1339
+ case M_SOF1: /* extended sequential DCT */
1340
+ {
1341
+ read_sof_marker();
1342
+ break;
1343
+ }
1344
+ case M_SOF9: /* Arithmitic coding */
1345
+ {
1346
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
1347
+ break;
1348
+ }
1349
+ default:
1350
+ {
1351
+ stop_decoding(JPGD_UNSUPPORTED_MARKER);
1352
+ break;
1353
+ }
1354
+ }
1355
+ }
1356
+
1357
+ // Find a start of scan (SOS) marker.
1358
+ int jpeg_decoder::locate_sos_marker()
1359
+ {
1360
+ int c;
1361
+
1362
+ c = process_markers();
1363
+
1364
+ if (c == M_EOI)
1365
+ return JPGD_FALSE;
1366
+ else if (c != M_SOS)
1367
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
1368
+
1369
+ read_sos_marker();
1370
+
1371
+ return JPGD_TRUE;
1372
+ }
1373
+
1374
+ // Reset everything to default/uninitialized state.
1375
+ void jpeg_decoder::init(jpeg_decoder_stream *pStream)
1376
+ {
1377
+ m_pMem_blocks = NULL;
1378
+ m_error_code = JPGD_SUCCESS;
1379
+ m_ready_flag = false;
1380
+ m_image_x_size = m_image_y_size = 0;
1381
+ m_pStream = pStream;
1382
+ m_progressive_flag = JPGD_FALSE;
1383
+
1384
+ memset(m_huff_ac, 0, sizeof(m_huff_ac));
1385
+ memset(m_huff_num, 0, sizeof(m_huff_num));
1386
+ memset(m_huff_val, 0, sizeof(m_huff_val));
1387
+ memset(m_quant, 0, sizeof(m_quant));
1388
+
1389
+ m_scan_type = 0;
1390
+ m_comps_in_frame = 0;
1391
+
1392
+ memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp));
1393
+ memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp));
1394
+ memset(m_comp_quant, 0, sizeof(m_comp_quant));
1395
+ memset(m_comp_ident, 0, sizeof(m_comp_ident));
1396
+ memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks));
1397
+ memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks));
1398
+
1399
+ m_comps_in_scan = 0;
1400
+ memset(m_comp_list, 0, sizeof(m_comp_list));
1401
+ memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab));
1402
+ memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab));
1403
+
1404
+ m_spectral_start = 0;
1405
+ m_spectral_end = 0;
1406
+ m_successive_low = 0;
1407
+ m_successive_high = 0;
1408
+ m_max_mcu_x_size = 0;
1409
+ m_max_mcu_y_size = 0;
1410
+ m_blocks_per_mcu = 0;
1411
+ m_max_blocks_per_row = 0;
1412
+ m_mcus_per_row = 0;
1413
+ m_mcus_per_col = 0;
1414
+ m_expanded_blocks_per_component = 0;
1415
+ m_expanded_blocks_per_mcu = 0;
1416
+ m_expanded_blocks_per_row = 0;
1417
+ m_freq_domain_chroma_upsample = false;
1418
+
1419
+ memset(m_mcu_org, 0, sizeof(m_mcu_org));
1420
+
1421
+ m_total_lines_left = 0;
1422
+ m_mcu_lines_left = 0;
1423
+ m_real_dest_bytes_per_scan_line = 0;
1424
+ m_dest_bytes_per_scan_line = 0;
1425
+ m_dest_bytes_per_pixel = 0;
1426
+
1427
+ memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs));
1428
+
1429
+ memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs));
1430
+ memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs));
1431
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
1432
+
1433
+ m_eob_run = 0;
1434
+
1435
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
1436
+
1437
+ m_pIn_buf_ofs = m_in_buf;
1438
+ m_in_buf_left = 0;
1439
+ m_eof_flag = false;
1440
+ m_tem_flag = 0;
1441
+
1442
+ memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start));
1443
+ memset(m_in_buf, 0, sizeof(m_in_buf));
1444
+ memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end));
1445
+
1446
+ m_restart_interval = 0;
1447
+ m_restarts_left = 0;
1448
+ m_next_restart_num = 0;
1449
+
1450
+ m_max_mcus_per_row = 0;
1451
+ m_max_blocks_per_mcu = 0;
1452
+ m_max_mcus_per_col = 0;
1453
+
1454
+ memset(m_last_dc_val, 0, sizeof(m_last_dc_val));
1455
+ m_pMCU_coefficients = NULL;
1456
+ m_pSample_buf = NULL;
1457
+
1458
+ m_total_bytes_read = 0;
1459
+
1460
+ m_pScan_line_0 = NULL;
1461
+ m_pScan_line_1 = NULL;
1462
+
1463
+ // Ready the input buffer.
1464
+ prep_in_buffer();
1465
+
1466
+ // Prime the bit buffer.
1467
+ m_bits_left = 16;
1468
+ m_bit_buf = 0;
1469
+
1470
+ get_bits(16);
1471
+ get_bits(16);
1472
+
1473
+ for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++)
1474
+ m_mcu_block_max_zag[i] = 64;
1475
+ }
1476
+
1477
+ #define SCALEBITS 16
1478
+ #define ONE_HALF ((int) 1 << (SCALEBITS-1))
1479
+ #define FIX(x) ((int) ((x) * (1L<<SCALEBITS) + 0.5f))
1480
+
1481
+ // Create a few tables that allow us to quickly convert YCbCr to RGB.
1482
+ void jpeg_decoder::create_look_ups()
1483
+ {
1484
+ for (int i = 0; i <= 255; i++)
1485
+ {
1486
+ int k = i - 128;
1487
+ m_crr[i] = ( FIX(1.40200f) * k + ONE_HALF) >> SCALEBITS;
1488
+ m_cbb[i] = ( FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS;
1489
+ m_crg[i] = (-FIX(0.71414f)) * k;
1490
+ m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF;
1491
+ }
1492
+ }
1493
+
1494
+ // This method throws back into the stream any bytes that where read
1495
+ // into the bit buffer during initial marker scanning.
1496
+ void jpeg_decoder::fix_in_buffer()
1497
+ {
1498
+ // In case any 0xFF's where pulled into the buffer during marker scanning.
1499
+ JPGD_ASSERT((m_bits_left & 7) == 0);
1500
+
1501
+ if (m_bits_left == 16)
1502
+ stuff_char( (uint8)(m_bit_buf & 0xFF));
1503
+
1504
+ if (m_bits_left >= 8)
1505
+ stuff_char( (uint8)((m_bit_buf >> 8) & 0xFF));
1506
+
1507
+ stuff_char((uint8)((m_bit_buf >> 16) & 0xFF));
1508
+ stuff_char((uint8)((m_bit_buf >> 24) & 0xFF));
1509
+
1510
+ m_bits_left = 16;
1511
+ get_bits_no_markers(16);
1512
+ get_bits_no_markers(16);
1513
+ }
1514
+
1515
+ void jpeg_decoder::transform_mcu(int mcu_row)
1516
+ {
1517
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
1518
+ uint8* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64;
1519
+
1520
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
1521
+ {
1522
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
1523
+ pSrc_ptr += 64;
1524
+ pDst_ptr += 64;
1525
+ }
1526
+ }
1527
+
1528
+ static const uint8 s_max_rc[64] =
1529
+ {
1530
+ 17, 18, 34, 50, 50, 51, 52, 52, 52, 68, 84, 84, 84, 84, 85, 86, 86, 86, 86, 86,
1531
+ 102, 118, 118, 118, 118, 118, 118, 119, 120, 120, 120, 120, 120, 120, 120, 136,
1532
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
1533
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136
1534
+ };
1535
+
1536
+ void jpeg_decoder::transform_mcu_expand(int mcu_row)
1537
+ {
1538
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
1539
+ uint8* pDst_ptr = m_pSample_buf + mcu_row * m_expanded_blocks_per_mcu * 64;
1540
+
1541
+ // Y IDCT
1542
+ int mcu_block;
1543
+ for (mcu_block = 0; mcu_block < m_expanded_blocks_per_component; mcu_block++)
1544
+ {
1545
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
1546
+ pSrc_ptr += 64;
1547
+ pDst_ptr += 64;
1548
+ }
1549
+
1550
+ // Chroma IDCT, with upsampling
1551
+ jpgd_block_t temp_block[64];
1552
+
1553
+ for (int i = 0; i < 2; i++)
1554
+ {
1555
+ DCT_Upsample::Matrix44 P, Q, R, S;
1556
+
1557
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1);
1558
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64);
1559
+
1560
+ switch (s_max_rc[m_mcu_block_max_zag[mcu_block++] - 1])
1561
+ {
1562
+ case 1*16+1:
1563
+ DCT_Upsample::P_Q<1, 1>::calc(P, Q, pSrc_ptr);
1564
+ DCT_Upsample::R_S<1, 1>::calc(R, S, pSrc_ptr);
1565
+ break;
1566
+ case 1*16+2:
1567
+ DCT_Upsample::P_Q<1, 2>::calc(P, Q, pSrc_ptr);
1568
+ DCT_Upsample::R_S<1, 2>::calc(R, S, pSrc_ptr);
1569
+ break;
1570
+ case 2*16+2:
1571
+ DCT_Upsample::P_Q<2, 2>::calc(P, Q, pSrc_ptr);
1572
+ DCT_Upsample::R_S<2, 2>::calc(R, S, pSrc_ptr);
1573
+ break;
1574
+ case 3*16+2:
1575
+ DCT_Upsample::P_Q<3, 2>::calc(P, Q, pSrc_ptr);
1576
+ DCT_Upsample::R_S<3, 2>::calc(R, S, pSrc_ptr);
1577
+ break;
1578
+ case 3*16+3:
1579
+ DCT_Upsample::P_Q<3, 3>::calc(P, Q, pSrc_ptr);
1580
+ DCT_Upsample::R_S<3, 3>::calc(R, S, pSrc_ptr);
1581
+ break;
1582
+ case 3*16+4:
1583
+ DCT_Upsample::P_Q<3, 4>::calc(P, Q, pSrc_ptr);
1584
+ DCT_Upsample::R_S<3, 4>::calc(R, S, pSrc_ptr);
1585
+ break;
1586
+ case 4*16+4:
1587
+ DCT_Upsample::P_Q<4, 4>::calc(P, Q, pSrc_ptr);
1588
+ DCT_Upsample::R_S<4, 4>::calc(R, S, pSrc_ptr);
1589
+ break;
1590
+ case 5*16+4:
1591
+ DCT_Upsample::P_Q<5, 4>::calc(P, Q, pSrc_ptr);
1592
+ DCT_Upsample::R_S<5, 4>::calc(R, S, pSrc_ptr);
1593
+ break;
1594
+ case 5*16+5:
1595
+ DCT_Upsample::P_Q<5, 5>::calc(P, Q, pSrc_ptr);
1596
+ DCT_Upsample::R_S<5, 5>::calc(R, S, pSrc_ptr);
1597
+ break;
1598
+ case 5*16+6:
1599
+ DCT_Upsample::P_Q<5, 6>::calc(P, Q, pSrc_ptr);
1600
+ DCT_Upsample::R_S<5, 6>::calc(R, S, pSrc_ptr);
1601
+ break;
1602
+ case 6*16+6:
1603
+ DCT_Upsample::P_Q<6, 6>::calc(P, Q, pSrc_ptr);
1604
+ DCT_Upsample::R_S<6, 6>::calc(R, S, pSrc_ptr);
1605
+ break;
1606
+ case 7*16+6:
1607
+ DCT_Upsample::P_Q<7, 6>::calc(P, Q, pSrc_ptr);
1608
+ DCT_Upsample::R_S<7, 6>::calc(R, S, pSrc_ptr);
1609
+ break;
1610
+ case 7*16+7:
1611
+ DCT_Upsample::P_Q<7, 7>::calc(P, Q, pSrc_ptr);
1612
+ DCT_Upsample::R_S<7, 7>::calc(R, S, pSrc_ptr);
1613
+ break;
1614
+ case 7*16+8:
1615
+ DCT_Upsample::P_Q<7, 8>::calc(P, Q, pSrc_ptr);
1616
+ DCT_Upsample::R_S<7, 8>::calc(R, S, pSrc_ptr);
1617
+ break;
1618
+ case 8*16+8:
1619
+ DCT_Upsample::P_Q<8, 8>::calc(P, Q, pSrc_ptr);
1620
+ DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
1621
+ break;
1622
+ default:
1623
+ JPGD_ASSERT(false);
1624
+ }
1625
+
1626
+ DCT_Upsample::Matrix44 a(P + Q); P -= Q;
1627
+ DCT_Upsample::Matrix44& b = P;
1628
+ DCT_Upsample::Matrix44 c(R + S); R -= S;
1629
+ DCT_Upsample::Matrix44& d = R;
1630
+
1631
+ DCT_Upsample::Matrix44::add_and_store(temp_block, a, c);
1632
+ idct_4x4(temp_block, pDst_ptr);
1633
+ pDst_ptr += 64;
1634
+
1635
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, a, c);
1636
+ idct_4x4(temp_block, pDst_ptr);
1637
+ pDst_ptr += 64;
1638
+
1639
+ DCT_Upsample::Matrix44::add_and_store(temp_block, b, d);
1640
+ idct_4x4(temp_block, pDst_ptr);
1641
+ pDst_ptr += 64;
1642
+
1643
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, b, d);
1644
+ idct_4x4(temp_block, pDst_ptr);
1645
+ pDst_ptr += 64;
1646
+
1647
+ pSrc_ptr += 64;
1648
+ }
1649
+ }
1650
+
1651
+ // Loads and dequantizes the next row of (already decoded) coefficients.
1652
+ // Progressive images only.
1653
+ void jpeg_decoder::load_next_row()
1654
+ {
1655
+ int i;
1656
+ jpgd_block_t *p;
1657
+ jpgd_quant_t *q;
1658
+ int mcu_row, mcu_block, row_block = 0;
1659
+ int component_num, component_id;
1660
+ int block_x_mcu[JPGD_MAX_COMPONENTS];
1661
+
1662
+ memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int));
1663
+
1664
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
1665
+ {
1666
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
1667
+
1668
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
1669
+ {
1670
+ component_id = m_mcu_org[mcu_block];
1671
+ q = m_quant[m_comp_quant[component_id]];
1672
+
1673
+ p = m_pMCU_coefficients + 64 * mcu_block;
1674
+
1675
+ jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
1676
+ jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
1677
+ p[0] = pDC[0];
1678
+ memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t));
1679
+
1680
+ for (i = 63; i > 0; i--)
1681
+ if (p[g_ZAG[i]])
1682
+ break;
1683
+
1684
+ m_mcu_block_max_zag[mcu_block] = i + 1;
1685
+
1686
+ for ( ; i >= 0; i--)
1687
+ if (p[g_ZAG[i]])
1688
+ p[g_ZAG[i]] = static_cast<jpgd_block_t>(p[g_ZAG[i]] * q[i]);
1689
+
1690
+ row_block++;
1691
+
1692
+ if (m_comps_in_scan == 1)
1693
+ block_x_mcu[component_id]++;
1694
+ else
1695
+ {
1696
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id])
1697
+ {
1698
+ block_x_mcu_ofs = 0;
1699
+
1700
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id])
1701
+ {
1702
+ block_y_mcu_ofs = 0;
1703
+
1704
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
1705
+ }
1706
+ }
1707
+ }
1708
+ }
1709
+
1710
+ if (m_freq_domain_chroma_upsample)
1711
+ transform_mcu_expand(mcu_row);
1712
+ else
1713
+ transform_mcu(mcu_row);
1714
+ }
1715
+
1716
+ if (m_comps_in_scan == 1)
1717
+ m_block_y_mcu[m_comp_list[0]]++;
1718
+ else
1719
+ {
1720
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++)
1721
+ {
1722
+ component_id = m_comp_list[component_num];
1723
+
1724
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
1725
+ }
1726
+ }
1727
+ }
1728
+
1729
+ // Restart interval processing.
1730
+ void jpeg_decoder::process_restart()
1731
+ {
1732
+ int i;
1733
+ int c = 0;
1734
+
1735
+ // Align to a byte boundry
1736
+ // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
1737
+ //get_bits_no_markers(m_bits_left & 7);
1738
+
1739
+ // Let's scan a little bit to find the marker, but not _too_ far.
1740
+ // 1536 is a "fudge factor" that determines how much to scan.
1741
+ for (i = 1536; i > 0; i--)
1742
+ if (get_char() == 0xFF)
1743
+ break;
1744
+
1745
+ if (i == 0)
1746
+ stop_decoding(JPGD_BAD_RESTART_MARKER);
1747
+
1748
+ for ( ; i > 0; i--)
1749
+ if ((c = get_char()) != 0xFF)
1750
+ break;
1751
+
1752
+ if (i == 0)
1753
+ stop_decoding(JPGD_BAD_RESTART_MARKER);
1754
+
1755
+ // Is it the expected marker? If not, something bad happened.
1756
+ if (c != (m_next_restart_num + M_RST0))
1757
+ stop_decoding(JPGD_BAD_RESTART_MARKER);
1758
+
1759
+ // Reset each component's DC prediction values.
1760
+ memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint));
1761
+
1762
+ m_eob_run = 0;
1763
+
1764
+ m_restarts_left = m_restart_interval;
1765
+
1766
+ m_next_restart_num = (m_next_restart_num + 1) & 7;
1767
+
1768
+ // Get the bit buffer going again...
1769
+
1770
+ m_bits_left = 16;
1771
+ get_bits_no_markers(16);
1772
+ get_bits_no_markers(16);
1773
+ }
1774
+
1775
+ static inline int dequantize_ac(int c, int q) { c *= q; return c; }
1776
+
1777
+ // Decodes and dequantizes the next row of coefficients.
1778
+ void jpeg_decoder::decode_next_row()
1779
+ {
1780
+ int row_block = 0;
1781
+
1782
+ for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
1783
+ {
1784
+ if ((m_restart_interval) && (m_restarts_left == 0))
1785
+ process_restart();
1786
+
1787
+ jpgd_block_t* p = m_pMCU_coefficients;
1788
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64)
1789
+ {
1790
+ int component_id = m_mcu_org[mcu_block];
1791
+ jpgd_quant_t* q = m_quant[m_comp_quant[component_id]];
1792
+
1793
+ int r, s;
1794
+ s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r);
1795
+ s = HUFF_EXTEND(r, s);
1796
+
1797
+ m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]);
1798
+
1799
+ p[0] = static_cast<jpgd_block_t>(s * q[0]);
1800
+
1801
+ int prev_num_set = m_mcu_block_max_zag[mcu_block];
1802
+
1803
+ huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]];
1804
+
1805
+ int k;
1806
+ for (k = 1; k < 64; k++)
1807
+ {
1808
+ int extra_bits;
1809
+ s = huff_decode(pH, extra_bits);
1810
+
1811
+ r = s >> 4;
1812
+ s &= 15;
1813
+
1814
+ if (s)
1815
+ {
1816
+ if (r)
1817
+ {
1818
+ if ((k + r) > 63)
1819
+ stop_decoding(JPGD_DECODE_ERROR);
1820
+
1821
+ if (k < prev_num_set)
1822
+ {
1823
+ int n = JPGD_MIN(r, prev_num_set - k);
1824
+ int kt = k;
1825
+ while (n--)
1826
+ p[g_ZAG[kt++]] = 0;
1827
+ }
1828
+
1829
+ k += r;
1830
+ }
1831
+
1832
+ s = HUFF_EXTEND(extra_bits, s);
1833
+
1834
+ JPGD_ASSERT(k < 64);
1835
+
1836
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
1837
+ }
1838
+ else
1839
+ {
1840
+ if (r == 15)
1841
+ {
1842
+ if ((k + 16) > 64)
1843
+ stop_decoding(JPGD_DECODE_ERROR);
1844
+
1845
+ if (k < prev_num_set)
1846
+ {
1847
+ int n = JPGD_MIN(16, prev_num_set - k);
1848
+ int kt = k;
1849
+ while (n--)
1850
+ {
1851
+ JPGD_ASSERT(kt <= 63);
1852
+ p[g_ZAG[kt++]] = 0;
1853
+ }
1854
+ }
1855
+
1856
+ k += 16 - 1; // - 1 because the loop counter is k
1857
+ // BEGIN EPIC MOD
1858
+ JPGD_ASSERT(k < 64 && p[g_ZAG[k]] == 0);
1859
+ // END EPIC MOD
1860
+ }
1861
+ else
1862
+ break;
1863
+ }
1864
+ }
1865
+
1866
+ if (k < prev_num_set)
1867
+ {
1868
+ int kt = k;
1869
+ while (kt < prev_num_set)
1870
+ p[g_ZAG[kt++]] = 0;
1871
+ }
1872
+
1873
+ m_mcu_block_max_zag[mcu_block] = k;
1874
+
1875
+ row_block++;
1876
+ }
1877
+
1878
+ if (m_freq_domain_chroma_upsample)
1879
+ transform_mcu_expand(mcu_row);
1880
+ else
1881
+ transform_mcu(mcu_row);
1882
+
1883
+ m_restarts_left--;
1884
+ }
1885
+ }
1886
+
1887
+ // YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB
1888
+ void jpeg_decoder::H1V1Convert()
1889
+ {
1890
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
1891
+ uint8 *d = m_pScan_line_0;
1892
+ uint8 *s = m_pSample_buf + row * 8;
1893
+
1894
+ for (int i = m_max_mcus_per_row; i > 0; i--)
1895
+ {
1896
+ for (int j = 0; j < 8; j++)
1897
+ {
1898
+ int y = s[j];
1899
+ int cb = s[64+j];
1900
+ int cr = s[128+j];
1901
+
1902
+ if (jpg_format == ERGBFormatJPG::BGRA)
1903
+ {
1904
+ d[0] = clamp(y + m_cbb[cb]);
1905
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
1906
+ d[2] = clamp(y + m_crr[cr]);
1907
+ d[3] = 255;
1908
+ }
1909
+ else
1910
+ {
1911
+ d[0] = clamp(y + m_crr[cr]);
1912
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
1913
+ d[2] = clamp(y + m_cbb[cb]);
1914
+ d[3] = 255;
1915
+ }
1916
+ d += 4;
1917
+ }
1918
+
1919
+ s += 64*3;
1920
+ }
1921
+ }
1922
+
1923
+ // YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB
1924
+ void jpeg_decoder::H2V1Convert()
1925
+ {
1926
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
1927
+ uint8 *d0 = m_pScan_line_0;
1928
+ uint8 *y = m_pSample_buf + row * 8;
1929
+ uint8 *c = m_pSample_buf + 2*64 + row * 8;
1930
+
1931
+ for (int i = m_max_mcus_per_row; i > 0; i--)
1932
+ {
1933
+ for (int l = 0; l < 2; l++)
1934
+ {
1935
+ for (int j = 0; j < 4; j++)
1936
+ {
1937
+ int cb = c[0];
1938
+ int cr = c[64];
1939
+
1940
+ int rc = m_crr[cr];
1941
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
1942
+ int bc = m_cbb[cb];
1943
+
1944
+ int yy = y[j<<1];
1945
+ if (jpg_format == ERGBFormatJPG::BGRA)
1946
+ {
1947
+ d0[0] = clamp(yy+bc);
1948
+ d0[1] = clamp(yy+gc);
1949
+ d0[2] = clamp(yy+rc);
1950
+ d0[3] = 255;
1951
+ yy = y[(j<<1)+1];
1952
+ d0[4] = clamp(yy+bc);
1953
+ d0[5] = clamp(yy+gc);
1954
+ d0[6] = clamp(yy+rc);
1955
+ d0[7] = 255;
1956
+ }
1957
+ else
1958
+ {
1959
+ d0[0] = clamp(yy+rc);
1960
+ d0[1] = clamp(yy+gc);
1961
+ d0[2] = clamp(yy+bc);
1962
+ d0[3] = 255;
1963
+ yy = y[(j<<1)+1];
1964
+ d0[4] = clamp(yy+rc);
1965
+ d0[5] = clamp(yy+gc);
1966
+ d0[6] = clamp(yy+bc);
1967
+ d0[7] = 255;
1968
+ }
1969
+
1970
+ d0 += 8;
1971
+
1972
+ c++;
1973
+ }
1974
+ y += 64;
1975
+ }
1976
+
1977
+ y += 64*4 - 64*2;
1978
+ c += 64*4 - 8;
1979
+ }
1980
+ }
1981
+
1982
+ // YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB
1983
+ void jpeg_decoder::H1V2Convert()
1984
+ {
1985
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
1986
+ uint8 *d0 = m_pScan_line_0;
1987
+ uint8 *d1 = m_pScan_line_1;
1988
+ uint8 *y;
1989
+ uint8 *c;
1990
+
1991
+ if (row < 8)
1992
+ y = m_pSample_buf + row * 8;
1993
+ else
1994
+ y = m_pSample_buf + 64*1 + (row & 7) * 8;
1995
+
1996
+ c = m_pSample_buf + 64*2 + (row >> 1) * 8;
1997
+
1998
+ for (int i = m_max_mcus_per_row; i > 0; i--)
1999
+ {
2000
+ for (int j = 0; j < 8; j++)
2001
+ {
2002
+ int cb = c[0+j];
2003
+ int cr = c[64+j];
2004
+
2005
+ int rc = m_crr[cr];
2006
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
2007
+ int bc = m_cbb[cb];
2008
+
2009
+ int yy = y[j];
2010
+ if (jpg_format == ERGBFormatJPG::BGRA)
2011
+ {
2012
+ d0[0] = clamp(yy+bc);
2013
+ d0[1] = clamp(yy+gc);
2014
+ d0[2] = clamp(yy+rc);
2015
+ d0[3] = 255;
2016
+ yy = y[8+j];
2017
+ d1[0] = clamp(yy+bc);
2018
+ d1[1] = clamp(yy+gc);
2019
+ d1[2] = clamp(yy+rc);
2020
+ d1[3] = 255;
2021
+ }
2022
+ else
2023
+ {
2024
+ d0[0] = clamp(yy+rc);
2025
+ d0[1] = clamp(yy+gc);
2026
+ d0[2] = clamp(yy+bc);
2027
+ d0[3] = 255;
2028
+ yy = y[8+j];
2029
+ d1[0] = clamp(yy+rc);
2030
+ d1[1] = clamp(yy+gc);
2031
+ d1[2] = clamp(yy+bc);
2032
+ d1[3] = 255;
2033
+ }
2034
+
2035
+ d0 += 4;
2036
+ d1 += 4;
2037
+ }
2038
+
2039
+ y += 64*4;
2040
+ c += 64*4;
2041
+ }
2042
+ }
2043
+
2044
+ // YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB
2045
+ void jpeg_decoder::H2V2Convert()
2046
+ {
2047
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
2048
+ uint8 *d0 = m_pScan_line_0;
2049
+ uint8 *d1 = m_pScan_line_1;
2050
+ uint8 *y;
2051
+ uint8 *c;
2052
+
2053
+ if (row < 8)
2054
+ y = m_pSample_buf + row * 8;
2055
+ else
2056
+ y = m_pSample_buf + 64*2 + (row & 7) * 8;
2057
+
2058
+ c = m_pSample_buf + 64*4 + (row >> 1) * 8;
2059
+
2060
+ for (int i = m_max_mcus_per_row; i > 0; i--)
2061
+ {
2062
+ for (int l = 0; l < 2; l++)
2063
+ {
2064
+ for (int j = 0; j < 8; j += 2)
2065
+ {
2066
+ int cb = c[0];
2067
+ int cr = c[64];
2068
+
2069
+ int rc = m_crr[cr];
2070
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
2071
+ int bc = m_cbb[cb];
2072
+
2073
+ int yy = y[j];
2074
+ if (jpg_format == ERGBFormatJPG::BGRA)
2075
+ {
2076
+ d0[0] = clamp(yy+bc);
2077
+ d0[1] = clamp(yy+gc);
2078
+ d0[2] = clamp(yy+rc);
2079
+ d0[3] = 255;
2080
+ yy = y[j+1];
2081
+ d0[4] = clamp(yy+bc);
2082
+ d0[5] = clamp(yy+gc);
2083
+ d0[6] = clamp(yy+rc);
2084
+ d0[7] = 255;
2085
+ yy = y[j+8];
2086
+ d1[0] = clamp(yy+bc);
2087
+ d1[1] = clamp(yy+gc);
2088
+ d1[2] = clamp(yy+rc);
2089
+ d1[3] = 255;
2090
+ yy = y[j+8+1];
2091
+ d1[4] = clamp(yy+bc);
2092
+ d1[5] = clamp(yy+gc);
2093
+ d1[6] = clamp(yy+rc);
2094
+ d1[7] = 255;
2095
+ }
2096
+ else
2097
+ {
2098
+ d0[0] = clamp(yy+rc);
2099
+ d0[1] = clamp(yy+gc);
2100
+ d0[2] = clamp(yy+bc);
2101
+ d0[3] = 255;
2102
+ yy = y[j+1];
2103
+ d0[4] = clamp(yy+rc);
2104
+ d0[5] = clamp(yy+gc);
2105
+ d0[6] = clamp(yy+bc);
2106
+ d0[7] = 255;
2107
+ yy = y[j+8];
2108
+ d1[0] = clamp(yy+rc);
2109
+ d1[1] = clamp(yy+gc);
2110
+ d1[2] = clamp(yy+bc);
2111
+ d1[3] = 255;
2112
+ yy = y[j+8+1];
2113
+ d1[4] = clamp(yy+rc);
2114
+ d1[5] = clamp(yy+gc);
2115
+ d1[6] = clamp(yy+bc);
2116
+ d1[7] = 255;
2117
+ }
2118
+
2119
+ d0 += 8;
2120
+ d1 += 8;
2121
+
2122
+ c++;
2123
+ }
2124
+ y += 64;
2125
+ }
2126
+
2127
+ y += 64*6 - 64*2;
2128
+ c += 64*6 - 8;
2129
+ }
2130
+ }
2131
+
2132
+ // Y (1 block per MCU) to 8-bit grayscale
2133
+ void jpeg_decoder::gray_convert()
2134
+ {
2135
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
2136
+ uint8 *d = m_pScan_line_0;
2137
+ uint8 *s = m_pSample_buf + row * 8;
2138
+
2139
+ for (int i = m_max_mcus_per_row; i > 0; i--)
2140
+ {
2141
+ *(uint *)d = *(uint *)s;
2142
+ *(uint *)(&d[4]) = *(uint *)(&s[4]);
2143
+
2144
+ s += 64;
2145
+ d += 8;
2146
+ }
2147
+ }
2148
+
2149
+ void jpeg_decoder::expanded_convert()
2150
+ {
2151
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
2152
+
2153
+ uint8* Py = m_pSample_buf + (row / 8) * 64 * m_comp_h_samp[0] + (row & 7) * 8;
2154
+
2155
+ uint8* d = m_pScan_line_0;
2156
+
2157
+ for (int i = m_max_mcus_per_row; i > 0; i--)
2158
+ {
2159
+ for (int k = 0; k < m_max_mcu_x_size; k += 8)
2160
+ {
2161
+ const int Y_ofs = k * 8;
2162
+ const int Cb_ofs = Y_ofs + 64 * m_expanded_blocks_per_component;
2163
+ const int Cr_ofs = Y_ofs + 64 * m_expanded_blocks_per_component * 2;
2164
+ for (int j = 0; j < 8; j++)
2165
+ {
2166
+ int y = Py[Y_ofs + j];
2167
+ int cb = Py[Cb_ofs + j];
2168
+ int cr = Py[Cr_ofs + j];
2169
+
2170
+ if (jpg_format == ERGBFormatJPG::BGRA)
2171
+ {
2172
+ d[0] = clamp(y + m_cbb[cb]);
2173
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
2174
+ d[2] = clamp(y + m_crr[cr]);
2175
+ d[3] = 255;
2176
+ }
2177
+ else
2178
+ {
2179
+ d[0] = clamp(y + m_crr[cr]);
2180
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
2181
+ d[2] = clamp(y + m_cbb[cb]);
2182
+ d[3] = 255;
2183
+ }
2184
+
2185
+ d += 4;
2186
+ }
2187
+ }
2188
+
2189
+ Py += 64 * m_expanded_blocks_per_mcu;
2190
+ }
2191
+ }
2192
+
2193
+ // Find end of image (EOI) marker, so we can return to the user the exact size of the input stream.
2194
+ void jpeg_decoder::find_eoi()
2195
+ {
2196
+ if (!m_progressive_flag)
2197
+ {
2198
+ // Attempt to read the EOI marker.
2199
+ //get_bits_no_markers(m_bits_left & 7);
2200
+
2201
+ // Prime the bit buffer
2202
+ m_bits_left = 16;
2203
+ get_bits(16);
2204
+ get_bits(16);
2205
+
2206
+ // The next marker _should_ be EOI
2207
+ process_markers();
2208
+ }
2209
+
2210
+ m_total_bytes_read -= m_in_buf_left;
2211
+ }
2212
+
2213
+ int jpeg_decoder::decode(const void** pScan_line, uint* pScan_line_len)
2214
+ {
2215
+ if ((m_error_code) || (!m_ready_flag))
2216
+ return JPGD_FAILED;
2217
+
2218
+ if (m_total_lines_left == 0)
2219
+ return JPGD_DONE;
2220
+
2221
+ if (m_mcu_lines_left == 0)
2222
+ {
2223
+ if (setjmp(m_jmp_state))
2224
+ return JPGD_FAILED;
2225
+
2226
+ if (m_progressive_flag)
2227
+ load_next_row();
2228
+ else
2229
+ decode_next_row();
2230
+
2231
+ // Find the EOI marker if that was the last row.
2232
+ if (m_total_lines_left <= m_max_mcu_y_size)
2233
+ find_eoi();
2234
+
2235
+ m_mcu_lines_left = m_max_mcu_y_size;
2236
+ }
2237
+
2238
+ if (m_freq_domain_chroma_upsample)
2239
+ {
2240
+ expanded_convert();
2241
+ *pScan_line = m_pScan_line_0;
2242
+ }
2243
+ else
2244
+ {
2245
+ switch (m_scan_type)
2246
+ {
2247
+ case JPGD_YH2V2:
2248
+ {
2249
+ if ((m_mcu_lines_left & 1) == 0)
2250
+ {
2251
+ H2V2Convert();
2252
+ *pScan_line = m_pScan_line_0;
2253
+ }
2254
+ else
2255
+ *pScan_line = m_pScan_line_1;
2256
+
2257
+ break;
2258
+ }
2259
+ case JPGD_YH2V1:
2260
+ {
2261
+ H2V1Convert();
2262
+ *pScan_line = m_pScan_line_0;
2263
+ break;
2264
+ }
2265
+ case JPGD_YH1V2:
2266
+ {
2267
+ if ((m_mcu_lines_left & 1) == 0)
2268
+ {
2269
+ H1V2Convert();
2270
+ *pScan_line = m_pScan_line_0;
2271
+ }
2272
+ else
2273
+ *pScan_line = m_pScan_line_1;
2274
+
2275
+ break;
2276
+ }
2277
+ case JPGD_YH1V1:
2278
+ {
2279
+ H1V1Convert();
2280
+ *pScan_line = m_pScan_line_0;
2281
+ break;
2282
+ }
2283
+ case JPGD_GRAYSCALE:
2284
+ {
2285
+ gray_convert();
2286
+ *pScan_line = m_pScan_line_0;
2287
+
2288
+ break;
2289
+ }
2290
+ }
2291
+ }
2292
+
2293
+ *pScan_line_len = m_real_dest_bytes_per_scan_line;
2294
+
2295
+ m_mcu_lines_left--;
2296
+ m_total_lines_left--;
2297
+
2298
+ return JPGD_SUCCESS;
2299
+ }
2300
+
2301
+ // Creates the tables needed for efficient Huffman decoding.
2302
+ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
2303
+ {
2304
+ int p, i, l, si;
2305
+ uint8 huffsize[257];
2306
+ uint huffcode[257];
2307
+ uint code;
2308
+ uint subtree;
2309
+ int code_size;
2310
+ int lastp;
2311
+ int nextfreeentry;
2312
+ int currententry;
2313
+
2314
+ pH->ac_table = m_huff_ac[index] != 0;
2315
+
2316
+ p = 0;
2317
+
2318
+ for (l = 1; l <= 16; l++)
2319
+ {
2320
+ for (i = 1; i <= m_huff_num[index][l]; i++)
2321
+ huffsize[p++] = static_cast<uint8>(l);
2322
+ }
2323
+
2324
+ huffsize[p] = 0;
2325
+
2326
+ lastp = p;
2327
+
2328
+ code = 0;
2329
+ si = huffsize[0];
2330
+ p = 0;
2331
+
2332
+ while (huffsize[p])
2333
+ {
2334
+ while (huffsize[p] == si)
2335
+ {
2336
+ huffcode[p++] = code;
2337
+ code++;
2338
+ }
2339
+
2340
+ code <<= 1;
2341
+ si++;
2342
+ }
2343
+
2344
+ memset(pH->look_up, 0, sizeof(pH->look_up));
2345
+ memset(pH->look_up2, 0, sizeof(pH->look_up2));
2346
+ memset(pH->tree, 0, sizeof(pH->tree));
2347
+ memset(pH->code_size, 0, sizeof(pH->code_size));
2348
+
2349
+ nextfreeentry = -1;
2350
+
2351
+ p = 0;
2352
+
2353
+ while (p < lastp)
2354
+ {
2355
+ i = m_huff_val[index][p];
2356
+ code = huffcode[p];
2357
+ code_size = huffsize[p];
2358
+
2359
+ pH->code_size[i] = static_cast<uint8>(code_size);
2360
+
2361
+ if (code_size <= 8)
2362
+ {
2363
+ code <<= (8 - code_size);
2364
+
2365
+ for (l = 1 << (8 - code_size); l > 0; l--)
2366
+ {
2367
+ JPGD_ASSERT(i < 256);
2368
+
2369
+ pH->look_up[code] = i;
2370
+
2371
+ bool has_extrabits = false;
2372
+ int extra_bits = 0;
2373
+ int num_extra_bits = i & 15;
2374
+
2375
+ int bits_to_fetch = code_size;
2376
+ if (num_extra_bits)
2377
+ {
2378
+ int total_codesize = code_size + num_extra_bits;
2379
+ if (total_codesize <= 8)
2380
+ {
2381
+ has_extrabits = true;
2382
+ extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize));
2383
+ JPGD_ASSERT(extra_bits <= 0x7FFF);
2384
+ bits_to_fetch += num_extra_bits;
2385
+ }
2386
+ }
2387
+
2388
+ if (!has_extrabits)
2389
+ pH->look_up2[code] = i | (bits_to_fetch << 8);
2390
+ else
2391
+ pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8);
2392
+
2393
+ code++;
2394
+ }
2395
+ }
2396
+ else
2397
+ {
2398
+ subtree = (code >> (code_size - 8)) & 0xFF;
2399
+
2400
+ currententry = pH->look_up[subtree];
2401
+
2402
+ if (currententry == 0)
2403
+ {
2404
+ pH->look_up[subtree] = currententry = nextfreeentry;
2405
+ pH->look_up2[subtree] = currententry = nextfreeentry;
2406
+
2407
+ nextfreeentry -= 2;
2408
+ }
2409
+
2410
+ code <<= (16 - (code_size - 8));
2411
+
2412
+ for (l = code_size; l > 9; l--)
2413
+ {
2414
+ if ((code & 0x8000) == 0)
2415
+ currententry--;
2416
+
2417
+ if (pH->tree[-currententry - 1] == 0)
2418
+ {
2419
+ pH->tree[-currententry - 1] = nextfreeentry;
2420
+
2421
+ currententry = nextfreeentry;
2422
+
2423
+ nextfreeentry -= 2;
2424
+ }
2425
+ else
2426
+ currententry = pH->tree[-currententry - 1];
2427
+
2428
+ code <<= 1;
2429
+ }
2430
+
2431
+ if ((code & 0x8000) == 0)
2432
+ currententry--;
2433
+
2434
+ pH->tree[-currententry - 1] = i;
2435
+ }
2436
+
2437
+ p++;
2438
+ }
2439
+ }
2440
+
2441
+ // Verifies the quantization tables needed for this scan are available.
2442
+ void jpeg_decoder::check_quant_tables()
2443
+ {
2444
+ for (int i = 0; i < m_comps_in_scan; i++)
2445
+ if (m_quant[m_comp_quant[m_comp_list[i]]] == NULL)
2446
+ stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
2447
+ }
2448
+
2449
+ // Verifies that all the Huffman tables needed for this scan are available.
2450
+ void jpeg_decoder::check_huff_tables()
2451
+ {
2452
+ for (int i = 0; i < m_comps_in_scan; i++)
2453
+ {
2454
+ if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == NULL))
2455
+ stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
2456
+
2457
+ if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == NULL))
2458
+ stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
2459
+ }
2460
+
2461
+ for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++)
2462
+ if (m_huff_num[i])
2463
+ {
2464
+ if (!m_pHuff_tabs[i])
2465
+ m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables));
2466
+
2467
+ make_huff_table(i, m_pHuff_tabs[i]);
2468
+ }
2469
+ }
2470
+
2471
+ // Determines the component order inside each MCU.
2472
+ // Also calcs how many MCU's are on each row, etc.
2473
+ void jpeg_decoder::calc_mcu_block_order()
2474
+ {
2475
+ int component_num, component_id;
2476
+ int max_h_samp = 0, max_v_samp = 0;
2477
+
2478
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++)
2479
+ {
2480
+ if (m_comp_h_samp[component_id] > max_h_samp)
2481
+ max_h_samp = m_comp_h_samp[component_id];
2482
+
2483
+ if (m_comp_v_samp[component_id] > max_v_samp)
2484
+ max_v_samp = m_comp_v_samp[component_id];
2485
+ }
2486
+
2487
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++)
2488
+ {
2489
+ m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8;
2490
+ m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8;
2491
+ }
2492
+
2493
+ if (m_comps_in_scan == 1)
2494
+ {
2495
+ m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]];
2496
+ m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]];
2497
+ }
2498
+ else
2499
+ {
2500
+ m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp;
2501
+ m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp;
2502
+ }
2503
+
2504
+ if (m_comps_in_scan == 1)
2505
+ {
2506
+ m_mcu_org[0] = m_comp_list[0];
2507
+
2508
+ m_blocks_per_mcu = 1;
2509
+ }
2510
+ else
2511
+ {
2512
+ m_blocks_per_mcu = 0;
2513
+
2514
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++)
2515
+ {
2516
+ int num_blocks;
2517
+
2518
+ component_id = m_comp_list[component_num];
2519
+
2520
+ num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id];
2521
+
2522
+ while (num_blocks--)
2523
+ m_mcu_org[m_blocks_per_mcu++] = component_id;
2524
+ }
2525
+ }
2526
+ }
2527
+
2528
+ // Starts a new scan.
2529
+ int jpeg_decoder::init_scan()
2530
+ {
2531
+ if (!locate_sos_marker())
2532
+ return JPGD_FALSE;
2533
+
2534
+ calc_mcu_block_order();
2535
+
2536
+ check_huff_tables();
2537
+
2538
+ check_quant_tables();
2539
+
2540
+ memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint));
2541
+
2542
+ m_eob_run = 0;
2543
+
2544
+ if (m_restart_interval)
2545
+ {
2546
+ m_restarts_left = m_restart_interval;
2547
+ m_next_restart_num = 0;
2548
+ }
2549
+
2550
+ fix_in_buffer();
2551
+
2552
+ return JPGD_TRUE;
2553
+ }
2554
+
2555
+ // Starts a frame. Determines if the number of components or sampling factors
2556
+ // are supported.
2557
+ void jpeg_decoder::init_frame()
2558
+ {
2559
+ int i;
2560
+
2561
+ if (m_comps_in_frame == 1)
2562
+ {
2563
+ if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1))
2564
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
2565
+
2566
+ m_scan_type = JPGD_GRAYSCALE;
2567
+ m_max_blocks_per_mcu = 1;
2568
+ m_max_mcu_x_size = 8;
2569
+ m_max_mcu_y_size = 8;
2570
+ }
2571
+ else if (m_comps_in_frame == 3)
2572
+ {
2573
+ if ( ((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) ||
2574
+ ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)) )
2575
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
2576
+
2577
+ if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
2578
+ {
2579
+ m_scan_type = JPGD_YH1V1;
2580
+
2581
+ m_max_blocks_per_mcu = 3;
2582
+ m_max_mcu_x_size = 8;
2583
+ m_max_mcu_y_size = 8;
2584
+ }
2585
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
2586
+ {
2587
+ m_scan_type = JPGD_YH2V1;
2588
+ m_max_blocks_per_mcu = 4;
2589
+ m_max_mcu_x_size = 16;
2590
+ m_max_mcu_y_size = 8;
2591
+ }
2592
+ else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2))
2593
+ {
2594
+ m_scan_type = JPGD_YH1V2;
2595
+ m_max_blocks_per_mcu = 4;
2596
+ m_max_mcu_x_size = 8;
2597
+ m_max_mcu_y_size = 16;
2598
+ }
2599
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
2600
+ {
2601
+ m_scan_type = JPGD_YH2V2;
2602
+ m_max_blocks_per_mcu = 6;
2603
+ m_max_mcu_x_size = 16;
2604
+ m_max_mcu_y_size = 16;
2605
+ }
2606
+ else
2607
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
2608
+ }
2609
+ else
2610
+ stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
2611
+
2612
+ m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
2613
+ m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
2614
+
2615
+ // These values are for the *destination* pixels: after conversion.
2616
+ if (m_scan_type == JPGD_GRAYSCALE)
2617
+ m_dest_bytes_per_pixel = 1;
2618
+ else
2619
+ m_dest_bytes_per_pixel = 4;
2620
+
2621
+ m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel;
2622
+
2623
+ m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel);
2624
+
2625
+ // Initialize two scan line buffers.
2626
+ m_pScan_line_0 = (uint8 *)alloc(m_dest_bytes_per_scan_line, true);
2627
+ if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2))
2628
+ m_pScan_line_1 = (uint8 *)alloc(m_dest_bytes_per_scan_line, true);
2629
+
2630
+ m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
2631
+
2632
+ // Should never happen
2633
+ if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW)
2634
+ stop_decoding(JPGD_ASSERTION_ERROR);
2635
+
2636
+ // Allocate the coefficient buffer, enough for one MCU
2637
+ m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
2638
+
2639
+ for (i = 0; i < m_max_blocks_per_mcu; i++)
2640
+ m_mcu_block_max_zag[i] = 64;
2641
+
2642
+ m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0];
2643
+ m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame;
2644
+ m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu;
2645
+ // Freq. domain chroma upsampling is only supported for H2V2 subsampling factor.
2646
+ // BEGIN EPIC MOD
2647
+ #if JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING
2648
+ m_freq_domain_chroma_upsample = (m_expanded_blocks_per_mcu == 4*3);
2649
+ #else
2650
+ m_freq_domain_chroma_upsample = 0;
2651
+ #endif
2652
+ // END EPIC MOD
2653
+
2654
+ if (m_freq_domain_chroma_upsample)
2655
+ m_pSample_buf = (uint8 *)alloc(m_expanded_blocks_per_row * 64);
2656
+ else
2657
+ m_pSample_buf = (uint8 *)alloc(m_max_blocks_per_row * 64);
2658
+
2659
+ m_total_lines_left = m_image_y_size;
2660
+
2661
+ m_mcu_lines_left = 0;
2662
+
2663
+ create_look_ups();
2664
+ }
2665
+
2666
+ // The coeff_buf series of methods originally stored the coefficients
2667
+ // into a "virtual" file which was located in EMS, XMS, or a disk file. A cache
2668
+ // was used to make this process more efficient. Now, we can store the entire
2669
+ // thing in RAM.
2670
+ jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y)
2671
+ {
2672
+ coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf));
2673
+
2674
+ cb->block_num_x = block_num_x;
2675
+ cb->block_num_y = block_num_y;
2676
+ cb->block_len_x = block_len_x;
2677
+ cb->block_len_y = block_len_y;
2678
+ cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t);
2679
+ cb->pData = (uint8 *)alloc(cb->block_size * block_num_x * block_num_y, true);
2680
+ return cb;
2681
+ }
2682
+
2683
+ inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y)
2684
+ {
2685
+ JPGD_ASSERT((block_x < cb->block_num_x) && (block_y < cb->block_num_y));
2686
+ return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x));
2687
+ }
2688
+
2689
+ // The following methods decode the various types of m_blocks encountered
2690
+ // in progressively encoded images.
2691
+ void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2692
+ {
2693
+ int s, r;
2694
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
2695
+
2696
+ if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0)
2697
+ {
2698
+ r = pD->get_bits_no_markers(s);
2699
+ s = HUFF_EXTEND(r, s);
2700
+ }
2701
+
2702
+ pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
2703
+
2704
+ p[0] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
2705
+ }
2706
+
2707
+ void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2708
+ {
2709
+ if (pD->get_bits_no_markers(1))
2710
+ {
2711
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
2712
+
2713
+ p[0] |= (1 << pD->m_successive_low);
2714
+ }
2715
+ }
2716
+
2717
+ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2718
+ {
2719
+ int k, s, r;
2720
+
2721
+ if (pD->m_eob_run)
2722
+ {
2723
+ pD->m_eob_run--;
2724
+ return;
2725
+ }
2726
+
2727
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
2728
+
2729
+ for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++)
2730
+ {
2731
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
2732
+
2733
+ r = s >> 4;
2734
+ s &= 15;
2735
+
2736
+ if (s)
2737
+ {
2738
+ if ((k += r) > 63)
2739
+ pD->stop_decoding(JPGD_DECODE_ERROR);
2740
+
2741
+ r = pD->get_bits_no_markers(s);
2742
+ s = HUFF_EXTEND(r, s);
2743
+
2744
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(s << pD->m_successive_low);
2745
+ }
2746
+ else
2747
+ {
2748
+ if (r == 15)
2749
+ {
2750
+ if ((k += 15) > 63)
2751
+ pD->stop_decoding(JPGD_DECODE_ERROR);
2752
+ }
2753
+ else
2754
+ {
2755
+ pD->m_eob_run = 1 << r;
2756
+
2757
+ if (r)
2758
+ pD->m_eob_run += pD->get_bits_no_markers(r);
2759
+
2760
+ pD->m_eob_run--;
2761
+
2762
+ break;
2763
+ }
2764
+ }
2765
+ }
2766
+ }
2767
+
2768
+ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
2769
+ {
2770
+ int s, k, r;
2771
+ int p1 = 1 << pD->m_successive_low;
2772
+ int m1 = (-1) << pD->m_successive_low;
2773
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
2774
+
2775
+ k = pD->m_spectral_start;
2776
+
2777
+ if (pD->m_eob_run == 0)
2778
+ {
2779
+ for ( ; k <= pD->m_spectral_end; k++)
2780
+ {
2781
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
2782
+
2783
+ r = s >> 4;
2784
+ s &= 15;
2785
+
2786
+ if (s)
2787
+ {
2788
+ if (s != 1)
2789
+ pD->stop_decoding(JPGD_DECODE_ERROR);
2790
+
2791
+ if (pD->get_bits_no_markers(1))
2792
+ s = p1;
2793
+ else
2794
+ s = m1;
2795
+ }
2796
+ else
2797
+ {
2798
+ if (r != 15)
2799
+ {
2800
+ pD->m_eob_run = 1 << r;
2801
+
2802
+ if (r)
2803
+ pD->m_eob_run += pD->get_bits_no_markers(r);
2804
+
2805
+ break;
2806
+ }
2807
+ }
2808
+
2809
+ do
2810
+ {
2811
+ // BEGIN EPIC MOD
2812
+ JPGD_ASSERT(k < 64);
2813
+ // END EPIC MOD
2814
+
2815
+ jpgd_block_t *this_coef = p + g_ZAG[k];
2816
+
2817
+ if (*this_coef != 0)
2818
+ {
2819
+ if (pD->get_bits_no_markers(1))
2820
+ {
2821
+ if ((*this_coef & p1) == 0)
2822
+ {
2823
+ if (*this_coef >= 0)
2824
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
2825
+ else
2826
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
2827
+ }
2828
+ }
2829
+ }
2830
+ else
2831
+ {
2832
+ if (--r < 0)
2833
+ break;
2834
+ }
2835
+
2836
+ k++;
2837
+
2838
+ } while (k <= pD->m_spectral_end);
2839
+
2840
+ if ((s) && (k < 64))
2841
+ {
2842
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(s);
2843
+ }
2844
+ }
2845
+ }
2846
+
2847
+ if (pD->m_eob_run > 0)
2848
+ {
2849
+ for ( ; k <= pD->m_spectral_end; k++)
2850
+ {
2851
+ // BEGIN EPIC MOD
2852
+ JPGD_ASSERT(k < 64);
2853
+ // END EPIC MOD
2854
+
2855
+ jpgd_block_t *this_coef = p + g_ZAG[k];
2856
+
2857
+ if (*this_coef != 0)
2858
+ {
2859
+ if (pD->get_bits_no_markers(1))
2860
+ {
2861
+ if ((*this_coef & p1) == 0)
2862
+ {
2863
+ if (*this_coef >= 0)
2864
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
2865
+ else
2866
+ *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
2867
+ }
2868
+ }
2869
+ }
2870
+ }
2871
+
2872
+ pD->m_eob_run--;
2873
+ }
2874
+ }
2875
+
2876
+ // Decode a scan in a progressively encoded image.
2877
+ void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
2878
+ {
2879
+ int mcu_row, mcu_col, mcu_block;
2880
+ int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS];
2881
+
2882
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
2883
+
2884
+ for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++)
2885
+ {
2886
+ int component_num, component_id;
2887
+
2888
+ memset(block_x_mcu, 0, sizeof(block_x_mcu));
2889
+
2890
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++)
2891
+ {
2892
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
2893
+
2894
+ if ((m_restart_interval) && (m_restarts_left == 0))
2895
+ process_restart();
2896
+
2897
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++)
2898
+ {
2899
+ component_id = m_mcu_org[mcu_block];
2900
+
2901
+ decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
2902
+
2903
+ if (m_comps_in_scan == 1)
2904
+ block_x_mcu[component_id]++;
2905
+ else
2906
+ {
2907
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id])
2908
+ {
2909
+ block_x_mcu_ofs = 0;
2910
+
2911
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id])
2912
+ {
2913
+ block_y_mcu_ofs = 0;
2914
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
2915
+ }
2916
+ }
2917
+ }
2918
+ }
2919
+
2920
+ m_restarts_left--;
2921
+ }
2922
+
2923
+ if (m_comps_in_scan == 1)
2924
+ m_block_y_mcu[m_comp_list[0]]++;
2925
+ else
2926
+ {
2927
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++)
2928
+ {
2929
+ component_id = m_comp_list[component_num];
2930
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
2931
+ }
2932
+ }
2933
+ }
2934
+ }
2935
+
2936
+ // Decode a progressively encoded image.
2937
+ void jpeg_decoder::init_progressive()
2938
+ {
2939
+ int i;
2940
+
2941
+ if (m_comps_in_frame == 4)
2942
+ stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
2943
+
2944
+ // Allocate the coefficient buffers.
2945
+ for (i = 0; i < m_comps_in_frame; i++)
2946
+ {
2947
+ m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1);
2948
+ m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8);
2949
+ }
2950
+
2951
+ for ( ; ; )
2952
+ {
2953
+ int dc_only_scan, refinement_scan;
2954
+ pDecode_block_func decode_block_func;
2955
+
2956
+ if (!init_scan())
2957
+ break;
2958
+
2959
+ dc_only_scan = (m_spectral_start == 0);
2960
+ refinement_scan = (m_successive_high != 0);
2961
+
2962
+ if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63))
2963
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
2964
+
2965
+ if (dc_only_scan)
2966
+ {
2967
+ if (m_spectral_end)
2968
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
2969
+ }
2970
+ else if (m_comps_in_scan != 1) /* AC scans can only contain one component */
2971
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
2972
+
2973
+ if ((refinement_scan) && (m_successive_low != m_successive_high - 1))
2974
+ stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
2975
+
2976
+ if (dc_only_scan)
2977
+ {
2978
+ if (refinement_scan)
2979
+ decode_block_func = decode_block_dc_refine;
2980
+ else
2981
+ decode_block_func = decode_block_dc_first;
2982
+ }
2983
+ else
2984
+ {
2985
+ if (refinement_scan)
2986
+ decode_block_func = decode_block_ac_refine;
2987
+ else
2988
+ decode_block_func = decode_block_ac_first;
2989
+ }
2990
+
2991
+ decode_scan(decode_block_func);
2992
+
2993
+ m_bits_left = 16;
2994
+ get_bits(16);
2995
+ get_bits(16);
2996
+ }
2997
+
2998
+ m_comps_in_scan = m_comps_in_frame;
2999
+
3000
+ for (i = 0; i < m_comps_in_frame; i++)
3001
+ m_comp_list[i] = i;
3002
+
3003
+ calc_mcu_block_order();
3004
+ }
3005
+
3006
+ void jpeg_decoder::init_sequential()
3007
+ {
3008
+ if (!init_scan())
3009
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
3010
+ }
3011
+
3012
+ void jpeg_decoder::decode_start()
3013
+ {
3014
+ init_frame();
3015
+
3016
+ if (m_progressive_flag)
3017
+ init_progressive();
3018
+ else
3019
+ init_sequential();
3020
+ }
3021
+
3022
+ void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
3023
+ {
3024
+ init(pStream);
3025
+ locate_sof_marker();
3026
+ }
3027
+
3028
+ jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
3029
+ {
3030
+ if (setjmp(m_jmp_state))
3031
+ return;
3032
+ decode_init(pStream);
3033
+ }
3034
+
3035
+ int jpeg_decoder::begin_decoding()
3036
+ {
3037
+ if (m_ready_flag)
3038
+ return JPGD_SUCCESS;
3039
+
3040
+ if (m_error_code)
3041
+ return JPGD_FAILED;
3042
+
3043
+ if (setjmp(m_jmp_state))
3044
+ return JPGD_FAILED;
3045
+
3046
+ decode_start();
3047
+
3048
+ m_ready_flag = true;
3049
+
3050
+ return JPGD_SUCCESS;
3051
+ }
3052
+
3053
+ jpeg_decoder::~jpeg_decoder()
3054
+ {
3055
+ free_all_blocks();
3056
+ }
3057
+
3058
+ jpeg_decoder_file_stream::jpeg_decoder_file_stream()
3059
+ {
3060
+ m_pFile = NULL;
3061
+ m_eof_flag = false;
3062
+ m_error_flag = false;
3063
+ }
3064
+
3065
+ void jpeg_decoder_file_stream::close()
3066
+ {
3067
+ if (m_pFile)
3068
+ {
3069
+ fclose(m_pFile);
3070
+ m_pFile = NULL;
3071
+ }
3072
+
3073
+ m_eof_flag = false;
3074
+ m_error_flag = false;
3075
+ }
3076
+
3077
+ jpeg_decoder_file_stream::~jpeg_decoder_file_stream()
3078
+ {
3079
+ close();
3080
+ }
3081
+
3082
+ bool jpeg_decoder_file_stream::open(const char *Pfilename)
3083
+ {
3084
+ close();
3085
+
3086
+ m_eof_flag = false;
3087
+ m_error_flag = false;
3088
+
3089
+ #if defined(_MSC_VER)
3090
+ m_pFile = NULL;
3091
+ fopen_s(&m_pFile, Pfilename, "rb");
3092
+ #else
3093
+ m_pFile = fopen(Pfilename, "rb");
3094
+ #endif
3095
+ return m_pFile != NULL;
3096
+ }
3097
+
3098
+ int jpeg_decoder_file_stream::read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag)
3099
+ {
3100
+ if (!m_pFile)
3101
+ return -1;
3102
+
3103
+ if (m_eof_flag)
3104
+ {
3105
+ *pEOF_flag = true;
3106
+ return 0;
3107
+ }
3108
+
3109
+ if (m_error_flag)
3110
+ return -1;
3111
+
3112
+ int bytes_read = static_cast<int>(fread(pBuf, 1, max_bytes_to_read, m_pFile));
3113
+ if (bytes_read < max_bytes_to_read)
3114
+ {
3115
+ if (ferror(m_pFile))
3116
+ {
3117
+ m_error_flag = true;
3118
+ return -1;
3119
+ }
3120
+
3121
+ m_eof_flag = true;
3122
+ *pEOF_flag = true;
3123
+ }
3124
+
3125
+ return bytes_read;
3126
+ }
3127
+
3128
+ bool jpeg_decoder_mem_stream::open(const uint8 *pSrc_data, uint size)
3129
+ {
3130
+ close();
3131
+ m_pSrc_data = pSrc_data;
3132
+ m_ofs = 0;
3133
+ m_size = size;
3134
+ return true;
3135
+ }
3136
+
3137
+ int jpeg_decoder_mem_stream::read(uint8 *pBuf, int max_bytes_to_read, bool *pEOF_flag)
3138
+ {
3139
+ *pEOF_flag = false;
3140
+
3141
+ if (!m_pSrc_data)
3142
+ return -1;
3143
+
3144
+ uint bytes_remaining = m_size - m_ofs;
3145
+ if ((uint)max_bytes_to_read > bytes_remaining)
3146
+ {
3147
+ max_bytes_to_read = bytes_remaining;
3148
+ *pEOF_flag = true;
3149
+ }
3150
+
3151
+ memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read);
3152
+ m_ofs += max_bytes_to_read;
3153
+
3154
+ return max_bytes_to_read;
3155
+ }
3156
+
3157
+ unsigned char *decompress_jpeg_image_from_stream(jpeg_decoder_stream *pStream, int *width, int *height, int *actual_comps, int req_comps)
3158
+ {
3159
+ if (!actual_comps)
3160
+ return NULL;
3161
+ *actual_comps = 0;
3162
+
3163
+ if ((!pStream) || (!width) || (!height) || (!req_comps))
3164
+ return NULL;
3165
+
3166
+ if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4))
3167
+ return NULL;
3168
+
3169
+ jpeg_decoder decoder(pStream);
3170
+ if (decoder.get_error_code() != JPGD_SUCCESS)
3171
+ return NULL;
3172
+
3173
+ const int image_width = decoder.get_width(), image_height = decoder.get_height();
3174
+ *width = image_width;
3175
+ *height = image_height;
3176
+ *actual_comps = decoder.get_num_components();
3177
+
3178
+ if (decoder.begin_decoding() != JPGD_SUCCESS)
3179
+ return NULL;
3180
+
3181
+ const int dst_bpl = image_width * req_comps;
3182
+
3183
+ uint8 *pImage_data = (uint8*)jpgd_malloc(dst_bpl * image_height);
3184
+ if (!pImage_data)
3185
+ return NULL;
3186
+
3187
+ for (int y = 0; y < image_height; y++)
3188
+ {
3189
+ const uint8* pScan_line = 0;
3190
+ uint scan_line_len;
3191
+ if (decoder.decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS)
3192
+ {
3193
+ jpgd_free(pImage_data);
3194
+ return NULL;
3195
+ }
3196
+
3197
+ uint8 *pDst = pImage_data + y * dst_bpl;
3198
+
3199
+ if (((req_comps == 4) && (decoder.get_num_components() == 3)) ||
3200
+ ((req_comps == 1) && (decoder.get_num_components() == 1)))
3201
+ {
3202
+ memcpy(pDst, pScan_line, dst_bpl);
3203
+ }
3204
+ else if (decoder.get_num_components() == 1)
3205
+ {
3206
+ if (req_comps == 3)
3207
+ {
3208
+ for (int x = 0; x < image_width; x++)
3209
+ {
3210
+ uint8 luma = pScan_line[x];
3211
+ pDst[0] = luma;
3212
+ pDst[1] = luma;
3213
+ pDst[2] = luma;
3214
+ pDst += 3;
3215
+ }
3216
+ }
3217
+ else
3218
+ {
3219
+ for (int x = 0; x < image_width; x++)
3220
+ {
3221
+ uint8 luma = pScan_line[x];
3222
+ pDst[0] = luma;
3223
+ pDst[1] = luma;
3224
+ pDst[2] = luma;
3225
+ pDst[3] = 255;
3226
+ pDst += 4;
3227
+ }
3228
+ }
3229
+ }
3230
+ else if (decoder.get_num_components() == 3)
3231
+ {
3232
+ if (req_comps == 1)
3233
+ {
3234
+ const int YR = 19595, YG = 38470, YB = 7471;
3235
+ for (int x = 0; x < image_width; x++)
3236
+ {
3237
+ int r = pScan_line[x*4+0];
3238
+ int g = pScan_line[x*4+1];
3239
+ int b = pScan_line[x*4+2];
3240
+ *pDst++ = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
3241
+ }
3242
+ }
3243
+ else
3244
+ {
3245
+ for (int x = 0; x < image_width; x++)
3246
+ {
3247
+ pDst[0] = pScan_line[x*4+0];
3248
+ pDst[1] = pScan_line[x*4+1];
3249
+ pDst[2] = pScan_line[x*4+2];
3250
+ pDst += 3;
3251
+ }
3252
+ }
3253
+ }
3254
+ }
3255
+
3256
+ return pImage_data;
3257
+ }
3258
+
3259
+ // BEGIN EPIC MOD
3260
+ unsigned char *decompress_jpeg_image_from_memory(const unsigned char *pSrc_data, int src_data_size, int *width, int *height, int *actual_comps, int req_comps, int format)
3261
+ {
3262
+ jpg_format = (ERGBFormatJPG)format;
3263
+ // EMD EPIC MOD
3264
+ jpgd::jpeg_decoder_mem_stream mem_stream(pSrc_data, src_data_size);
3265
+ return decompress_jpeg_image_from_stream(&mem_stream, width, height, actual_comps, req_comps);
3266
+ }
3267
+
3268
+ unsigned char *decompress_jpeg_image_from_file(const char *pSrc_filename, int *width, int *height, int *actual_comps, int req_comps)
3269
+ {
3270
+ jpgd::jpeg_decoder_file_stream file_stream;
3271
+ if (!file_stream.open(pSrc_filename))
3272
+ return NULL;
3273
+ return decompress_jpeg_image_from_stream(&file_stream, width, height, actual_comps, req_comps);
3274
+ }
3275
+
3276
+ } // namespace jpgd
crazy_functions/test_project/cpp/longcode/jpge.cpp ADDED
@@ -0,0 +1,1049 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // jpge.cpp - C++ class for JPEG compression.
2
+ // Public domain, Rich Geldreich <richgel99@gmail.com>
3
+ // v1.01, Dec. 18, 2010 - Initial release
4
+ // v1.02, Apr. 6, 2011 - Removed 2x2 ordered dither in H2V1 chroma subsampling method load_block_16_8_8(). (The rounding factor was 2, when it should have been 1. Either way, it wasn't helping.)
5
+ // v1.03, Apr. 16, 2011 - Added support for optimized Huffman code tables, optimized dynamic memory allocation down to only 1 alloc.
6
+ // Also from Alex Evans: Added RGBA support, linear memory allocator (no longer needed in v1.03).
7
+ // v1.04, May. 19, 2012: Forgot to set m_pFile ptr to NULL in cfile_stream::close(). Thanks to Owen Kaluza for reporting this bug.
8
+ // Code tweaks to fix VS2008 static code analysis warnings (all looked harmless).
9
+ // Code review revealed method load_block_16_8_8() (used for the non-default H2V1 sampling mode to downsample chroma) somehow didn't get the rounding factor fix from v1.02.
10
+
11
+ #include "jpge.h"
12
+
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+ #if PLATFORM_WINDOWS
16
+ #include <malloc.h>
17
+ #endif
18
+
19
+ #define JPGE_MAX(a,b) (((a)>(b))?(a):(b))
20
+ #define JPGE_MIN(a,b) (((a)<(b))?(a):(b))
21
+
22
+ namespace jpge {
23
+
24
+ static inline void *jpge_malloc(size_t nSize) { return FMemory::Malloc(nSize); }
25
+ static inline void jpge_free(void *p) { FMemory::Free(p);; }
26
+
27
+ // Various JPEG enums and tables.
28
+ enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 };
29
+ enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 };
30
+
31
+ static uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
32
+ static int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 };
33
+ static int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 };
34
+ static uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 };
35
+ static uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
36
+ static uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d };
37
+ static uint8 s_ac_lum_val[AC_LUM_CODES] =
38
+ {
39
+ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
40
+ 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
41
+ 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
42
+ 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
43
+ 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
44
+ 0xf9,0xfa
45
+ };
46
+ static uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };
47
+ static uint8 s_dc_chroma_val[DC_CHROMA_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
48
+ static uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 };
49
+ static uint8 s_ac_chroma_val[AC_CHROMA_CODES] =
50
+ {
51
+ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
52
+ 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
53
+ 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
54
+ 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
55
+ 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
56
+ 0xf9,0xfa
57
+ };
58
+
59
+ // Low-level helper functions.
60
+ template <class T> inline void clear_obj(T &obj) { memset(&obj, 0, sizeof(obj)); }
61
+
62
+ const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
63
+ static inline uint8 clamp(int i) { if (static_cast<uint>(i) > 255U) { if (i < 0) i = 0; else if (i > 255) i = 255; } return static_cast<uint8>(i); }
64
+
65
+ static void RGB_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels)
66
+ {
67
+ for ( ; num_pixels; pDst += 3, pSrc += 3, num_pixels--)
68
+ {
69
+ const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
70
+ pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
71
+ pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
72
+ pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
73
+ }
74
+ }
75
+
76
+ static void RGB_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels)
77
+ {
78
+ for ( ; num_pixels; pDst++, pSrc += 3, num_pixels--)
79
+ pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
80
+ }
81
+
82
+ static void RGBA_to_YCC(uint8* pDst, const uint8 *pSrc, int num_pixels)
83
+ {
84
+ for ( ; num_pixels; pDst += 3, pSrc += 4, num_pixels--)
85
+ {
86
+ const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
87
+ pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
88
+ pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
89
+ pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
90
+ }
91
+ }
92
+
93
+ static void RGBA_to_Y(uint8* pDst, const uint8 *pSrc, int num_pixels)
94
+ {
95
+ for ( ; num_pixels; pDst++, pSrc += 4, num_pixels--)
96
+ pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
97
+ }
98
+
99
+ static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels)
100
+ {
101
+ for( ; num_pixels; pDst += 3, pSrc++, num_pixels--) { pDst[0] = pSrc[0]; pDst[1] = 128; pDst[2] = 128; }
102
+ }
103
+
104
+ // Forward DCT - DCT derived from jfdctint.
105
+ #define CONST_BITS 13
106
+ #define ROW_BITS 2
107
+ #define DCT_DESCALE(x, n) (((x) + (((int32)1) << ((n) - 1))) >> (n))
108
+ #define DCT_MUL(var, c) (static_cast<int16>(var) * static_cast<int32>(c))
109
+ #define DCT1D(s0, s1, s2, s3, s4, s5, s6, s7) \
110
+ int32 t0 = s0 + s7, t7 = s0 - s7, t1 = s1 + s6, t6 = s1 - s6, t2 = s2 + s5, t5 = s2 - s5, t3 = s3 + s4, t4 = s3 - s4; \
111
+ int32 t10 = t0 + t3, t13 = t0 - t3, t11 = t1 + t2, t12 = t1 - t2; \
112
+ int32 u1 = DCT_MUL(t12 + t13, 4433); \
113
+ s2 = u1 + DCT_MUL(t13, 6270); \
114
+ s6 = u1 + DCT_MUL(t12, -15137); \
115
+ u1 = t4 + t7; \
116
+ int32 u2 = t5 + t6, u3 = t4 + t6, u4 = t5 + t7; \
117
+ int32 z5 = DCT_MUL(u3 + u4, 9633); \
118
+ t4 = DCT_MUL(t4, 2446); t5 = DCT_MUL(t5, 16819); \
119
+ t6 = DCT_MUL(t6, 25172); t7 = DCT_MUL(t7, 12299); \
120
+ u1 = DCT_MUL(u1, -7373); u2 = DCT_MUL(u2, -20995); \
121
+ u3 = DCT_MUL(u3, -16069); u4 = DCT_MUL(u4, -3196); \
122
+ u3 += z5; u4 += z5; \
123
+ s0 = t10 + t11; s1 = t7 + u1 + u4; s3 = t6 + u2 + u3; s4 = t10 - t11; s5 = t5 + u2 + u4; s7 = t4 + u1 + u3;
124
+
125
+ static void DCT2D(int32 *p)
126
+ {
127
+ int32 c, *q = p;
128
+ for (c = 7; c >= 0; c--, q += 8)
129
+ {
130
+ int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7];
131
+ DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
132
+ q[0] = s0 << ROW_BITS; q[1] = DCT_DESCALE(s1, CONST_BITS-ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS-ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS-ROW_BITS);
133
+ q[4] = s4 << ROW_BITS; q[5] = DCT_DESCALE(s5, CONST_BITS-ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS-ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS-ROW_BITS);
134
+ }
135
+ for (q = p, c = 7; c >= 0; c--, q++)
136
+ {
137
+ int32 s0 = q[0*8], s1 = q[1*8], s2 = q[2*8], s3 = q[3*8], s4 = q[4*8], s5 = q[5*8], s6 = q[6*8], s7 = q[7*8];
138
+ DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
139
+ q[0*8] = DCT_DESCALE(s0, ROW_BITS+3); q[1*8] = DCT_DESCALE(s1, CONST_BITS+ROW_BITS+3); q[2*8] = DCT_DESCALE(s2, CONST_BITS+ROW_BITS+3); q[3*8] = DCT_DESCALE(s3, CONST_BITS+ROW_BITS+3);
140
+ q[4*8] = DCT_DESCALE(s4, ROW_BITS+3); q[5*8] = DCT_DESCALE(s5, CONST_BITS+ROW_BITS+3); q[6*8] = DCT_DESCALE(s6, CONST_BITS+ROW_BITS+3); q[7*8] = DCT_DESCALE(s7, CONST_BITS+ROW_BITS+3);
141
+ }
142
+ }
143
+
144
+ struct sym_freq { uint m_key, m_sym_index; };
145
+
146
+ // Radix sorts sym_freq[] array by 32-bit key m_key. Returns ptr to sorted values.
147
+ static inline sym_freq* radix_sort_syms(uint num_syms, sym_freq* pSyms0, sym_freq* pSyms1)
148
+ {
149
+ const uint cMaxPasses = 4;
150
+ uint32 hist[256 * cMaxPasses]; clear_obj(hist);
151
+ for (uint i = 0; i < num_syms; i++) { uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; hist[256*2 + ((freq >> 16) & 0xFF)]++; hist[256*3 + ((freq >> 24) & 0xFF)]++; }
152
+ sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1;
153
+ uint total_passes = cMaxPasses; while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
154
+ for (uint pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
155
+ {
156
+ const uint32* pHist = &hist[pass << 8];
157
+ uint offsets[256], cur_ofs = 0;
158
+ for (uint i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
159
+ for (uint i = 0; i < num_syms; i++)
160
+ pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
161
+ sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t;
162
+ }
163
+ return pCur_syms;
164
+ }
165
+
166
+ // calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.
167
+ static void calculate_minimum_redundancy(sym_freq *A, int n)
168
+ {
169
+ int root, leaf, next, avbl, used, dpth;
170
+ if (n==0) return; else if (n==1) { A[0].m_key = 1; return; }
171
+ A[0].m_key += A[1].m_key; root = 0; leaf = 2;
172
+ for (next=1; next < n-1; next++)
173
+ {
174
+ if (leaf>=n || A[root].m_key<A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = next; } else A[next].m_key = A[leaf++].m_key;
175
+ if (leaf>=n || (root<next && A[root].m_key<A[leaf].m_key)) { A[next].m_key += A[root].m_key; A[root++].m_key = next; } else A[next].m_key += A[leaf++].m_key;
176
+ }
177
+ A[n-2].m_key = 0;
178
+ for (next=n-3; next>=0; next--) A[next].m_key = A[A[next].m_key].m_key+1;
179
+ avbl = 1; used = dpth = 0; root = n-2; next = n-1;
180
+ while (avbl>0)
181
+ {
182
+ while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; }
183
+ while (avbl>used) { A[next--].m_key = dpth; avbl--; }
184
+ avbl = 2*used; dpth++; used = 0;
185
+ }
186
+ }
187
+
188
+ // Limits canonical Huffman code table's max code size to max_code_size.
189
+ static void huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)
190
+ {
191
+ if (code_list_len <= 1) return;
192
+
193
+ for (int i = max_code_size + 1; i <= MAX_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
194
+
195
+ uint32 total = 0;
196
+ for (int i = max_code_size; i > 0; i--)
197
+ total += (((uint32)pNum_codes[i]) << (max_code_size - i));
198
+
199
+ while (total != (1UL << max_code_size))
200
+ {
201
+ pNum_codes[max_code_size]--;
202
+ for (int i = max_code_size - 1; i > 0; i--)
203
+ {
204
+ if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
205
+ }
206
+ total--;
207
+ }
208
+ }
209
+
210
+ // Generates an optimized offman table.
211
+ void jpeg_encoder::optimize_huffman_table(int table_num, int table_len)
212
+ {
213
+ sym_freq syms0[MAX_HUFF_SYMBOLS], syms1[MAX_HUFF_SYMBOLS];
214
+ syms0[0].m_key = 1; syms0[0].m_sym_index = 0; // dummy symbol, assures that no valid code contains all 1's
215
+ int num_used_syms = 1;
216
+ const uint32 *pSym_count = &m_huff_count[table_num][0];
217
+ for (int i = 0; i < table_len; i++)
218
+ if (pSym_count[i]) { syms0[num_used_syms].m_key = pSym_count[i]; syms0[num_used_syms++].m_sym_index = i + 1; }
219
+ sym_freq* pSyms = radix_sort_syms(num_used_syms, syms0, syms1);
220
+ calculate_minimum_redundancy(pSyms, num_used_syms);
221
+
222
+ // Count the # of symbols of each code size.
223
+ int num_codes[1 + MAX_HUFF_CODESIZE]; clear_obj(num_codes);
224
+ for (int i = 0; i < num_used_syms; i++)
225
+ num_codes[pSyms[i].m_key]++;
226
+
227
+ const uint JPGE_CODE_SIZE_LIMIT = 16; // the maximum possible size of a JPEG Huffman code (valid range is [9,16] - 9 vs. 8 because of the dummy symbol)
228
+ huffman_enforce_max_code_size(num_codes, num_used_syms, JPGE_CODE_SIZE_LIMIT);
229
+
230
+ // Compute m_huff_bits array, which contains the # of symbols per code size.
231
+ clear_obj(m_huff_bits[table_num]);
232
+ for (int i = 1; i <= (int)JPGE_CODE_SIZE_LIMIT; i++)
233
+ m_huff_bits[table_num][i] = static_cast<uint8>(num_codes[i]);
234
+
235
+ // Remove the dummy symbol added above, which must be in largest bucket.
236
+ for (int i = JPGE_CODE_SIZE_LIMIT; i >= 1; i--)
237
+ {
238
+ if (m_huff_bits[table_num][i]) { m_huff_bits[table_num][i]--; break; }
239
+ }
240
+
241
+ // Compute the m_huff_val array, which contains the symbol indices sorted by code size (smallest to largest).
242
+ for (int i = num_used_syms - 1; i >= 1; i--)
243
+ m_huff_val[table_num][num_used_syms - 1 - i] = static_cast<uint8>(pSyms[i].m_sym_index - 1);
244
+ }
245
+
246
+ // JPEG marker generation.
247
+ void jpeg_encoder::emit_byte(uint8 i)
248
+ {
249
+ m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_obj(i);
250
+ }
251
+
252
+ void jpeg_encoder::emit_word(uint i)
253
+ {
254
+ emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF));
255
+ }
256
+
257
+ void jpeg_encoder::emit_marker(int marker)
258
+ {
259
+ emit_byte(uint8(0xFF)); emit_byte(uint8(marker));
260
+ }
261
+
262
+ // Emit JFIF marker
263
+ void jpeg_encoder::emit_jfif_app0()
264
+ {
265
+ emit_marker(M_APP0);
266
+ emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1);
267
+ emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */
268
+ emit_byte(0);
269
+ emit_byte(1); /* Major version */
270
+ emit_byte(1); /* Minor version */
271
+ emit_byte(0); /* Density unit */
272
+ emit_word(1);
273
+ emit_word(1);
274
+ emit_byte(0); /* No thumbnail image */
275
+ emit_byte(0);
276
+ }
277
+
278
+ // Emit quantization tables
279
+ void jpeg_encoder::emit_dqt()
280
+ {
281
+ for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++)
282
+ {
283
+ emit_marker(M_DQT);
284
+ emit_word(64 + 1 + 2);
285
+ emit_byte(static_cast<uint8>(i));
286
+ for (int j = 0; j < 64; j++)
287
+ emit_byte(static_cast<uint8>(m_quantization_tables[i][j]));
288
+ }
289
+ }
290
+
291
+ // Emit start of frame marker
292
+ void jpeg_encoder::emit_sof()
293
+ {
294
+ emit_marker(M_SOF0); /* baseline */
295
+ emit_word(3 * m_num_components + 2 + 5 + 1);
296
+ emit_byte(8); /* precision */
297
+ emit_word(m_image_y);
298
+ emit_word(m_image_x);
299
+ emit_byte(m_num_components);
300
+ for (int i = 0; i < m_num_components; i++)
301
+ {
302
+ emit_byte(static_cast<uint8>(i + 1)); /* component ID */
303
+ emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]); /* h and v sampling */
304
+ emit_byte(i > 0); /* quant. table num */
305
+ }
306
+ }
307
+
308
+ // Emit Huffman table.
309
+ void jpeg_encoder::emit_dht(uint8 *bits, uint8 *val, int index, bool ac_flag)
310
+ {
311
+ emit_marker(M_DHT);
312
+
313
+ int length = 0;
314
+ for (int i = 1; i <= 16; i++)
315
+ length += bits[i];
316
+
317
+ emit_word(length + 2 + 1 + 16);
318
+ emit_byte(static_cast<uint8>(index + (ac_flag << 4)));
319
+
320
+ for (int i = 1; i <= 16; i++)
321
+ emit_byte(bits[i]);
322
+
323
+ for (int i = 0; i < length; i++)
324
+ emit_byte(val[i]);
325
+ }
326
+
327
+ // Emit all Huffman tables.
328
+ void jpeg_encoder::emit_dhts()
329
+ {
330
+ emit_dht(m_huff_bits[0+0], m_huff_val[0+0], 0, false);
331
+ emit_dht(m_huff_bits[2+0], m_huff_val[2+0], 0, true);
332
+ if (m_num_components == 3)
333
+ {
334
+ emit_dht(m_huff_bits[0+1], m_huff_val[0+1], 1, false);
335
+ emit_dht(m_huff_bits[2+1], m_huff_val[2+1], 1, true);
336
+ }
337
+ }
338
+
339
+ // emit start of scan
340
+ void jpeg_encoder::emit_sos()
341
+ {
342
+ emit_marker(M_SOS);
343
+ emit_word(2 * m_num_components + 2 + 1 + 3);
344
+ emit_byte(m_num_components);
345
+ for (int i = 0; i < m_num_components; i++)
346
+ {
347
+ emit_byte(static_cast<uint8>(i + 1));
348
+ if (i == 0)
349
+ emit_byte((0 << 4) + 0);
350
+ else
351
+ emit_byte((1 << 4) + 1);
352
+ }
353
+ emit_byte(0); /* spectral selection */
354
+ emit_byte(63);
355
+ emit_byte(0);
356
+ }
357
+
358
+ // Emit all markers at beginning of image file.
359
+ void jpeg_encoder::emit_markers()
360
+ {
361
+ emit_marker(M_SOI);
362
+ emit_jfif_app0();
363
+ emit_dqt();
364
+ emit_sof();
365
+ emit_dhts();
366
+ emit_sos();
367
+ }
368
+
369
+ // Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays.
370
+ void jpeg_encoder::compute_huffman_table(uint *codes, uint8 *code_sizes, uint8 *bits, uint8 *val)
371
+ {
372
+ int i, l, last_p, si;
373
+ uint8 huff_size[257];
374
+ uint huff_code[257];
375
+ uint code;
376
+
377
+ int p = 0;
378
+ for (l = 1; l <= 16; l++)
379
+ for (i = 1; i <= bits[l]; i++)
380
+ huff_size[p++] = (char)l;
381
+
382
+ huff_size[p] = 0; last_p = p; // write sentinel
383
+
384
+ code = 0; si = huff_size[0]; p = 0;
385
+
386
+ while (huff_size[p])
387
+ {
388
+ while (huff_size[p] == si)
389
+ huff_code[p++] = code++;
390
+ code <<= 1;
391
+ si++;
392
+ }
393
+
394
+ memset(codes, 0, sizeof(codes[0])*256);
395
+ memset(code_sizes, 0, sizeof(code_sizes[0])*256);
396
+ for (p = 0; p < last_p; p++)
397
+ {
398
+ codes[val[p]] = huff_code[p];
399
+ code_sizes[val[p]] = huff_size[p];
400
+ }
401
+ }
402
+
403
+ // Quantization table generation.
404
+ void jpeg_encoder::compute_quant_table(int32 *pDst, int16 *pSrc)
405
+ {
406
+ int32 q;
407
+ if (m_params.m_quality < 50)
408
+ q = 5000 / m_params.m_quality;
409
+ else
410
+ q = 200 - m_params.m_quality * 2;
411
+ for (int i = 0; i < 64; i++)
412
+ {
413
+ int32 j = *pSrc++; j = (j * q + 50L) / 100L;
414
+ *pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255);
415
+ }
416
+ }
417
+
418
+ // Higher-level methods.
419
+ void jpeg_encoder::first_pass_init()
420
+ {
421
+ m_bit_buffer = 0; m_bits_in = 0;
422
+ memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0]));
423
+ m_mcu_y_ofs = 0;
424
+ m_pass_num = 1;
425
+ }
426
+
427
+ bool jpeg_encoder::second_pass_init()
428
+ {
429
+ compute_huffman_table(&m_huff_codes[0+0][0], &m_huff_code_sizes[0+0][0], m_huff_bits[0+0], m_huff_val[0+0]);
430
+ compute_huffman_table(&m_huff_codes[2+0][0], &m_huff_code_sizes[2+0][0], m_huff_bits[2+0], m_huff_val[2+0]);
431
+ if (m_num_components > 1)
432
+ {
433
+ compute_huffman_table(&m_huff_codes[0+1][0], &m_huff_code_sizes[0+1][0], m_huff_bits[0+1], m_huff_val[0+1]);
434
+ compute_huffman_table(&m_huff_codes[2+1][0], &m_huff_code_sizes[2+1][0], m_huff_bits[2+1], m_huff_val[2+1]);
435
+ }
436
+ first_pass_init();
437
+ emit_markers();
438
+ m_pass_num = 2;
439
+ return true;
440
+ }
441
+
442
+ bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels)
443
+ {
444
+ m_num_components = 3;
445
+ switch (m_params.m_subsampling)
446
+ {
447
+ case Y_ONLY:
448
+ {
449
+ m_num_components = 1;
450
+ m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
451
+ m_mcu_x = 8; m_mcu_y = 8;
452
+ break;
453
+ }
454
+ case H1V1:
455
+ {
456
+ m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
457
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
458
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
459
+ m_mcu_x = 8; m_mcu_y = 8;
460
+ break;
461
+ }
462
+ case H2V1:
463
+ {
464
+ m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1;
465
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
466
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
467
+ m_mcu_x = 16; m_mcu_y = 8;
468
+ break;
469
+ }
470
+ case H2V2:
471
+ {
472
+ m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2;
473
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
474
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
475
+ m_mcu_x = 16; m_mcu_y = 16;
476
+ }
477
+ }
478
+
479
+ m_image_x = p_x_res; m_image_y = p_y_res;
480
+ m_image_bpp = src_channels;
481
+ m_image_bpl = m_image_x * src_channels;
482
+ m_image_x_mcu = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1));
483
+ m_image_y_mcu = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1));
484
+ m_image_bpl_xlt = m_image_x * m_num_components;
485
+ m_image_bpl_mcu = m_image_x_mcu * m_num_components;
486
+ m_mcus_per_row = m_image_x_mcu / m_mcu_x;
487
+
488
+ if ((m_mcu_lines[0] = static_cast<uint8*>(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) return false;
489
+ for (int i = 1; i < m_mcu_y; i++)
490
+ m_mcu_lines[i] = m_mcu_lines[i-1] + m_image_bpl_mcu;
491
+
492
+ compute_quant_table(m_quantization_tables[0], s_std_lum_quant);
493
+ compute_quant_table(m_quantization_tables[1], m_params.m_no_chroma_discrim_flag ? s_std_lum_quant : s_std_croma_quant);
494
+
495
+ m_out_buf_left = JPGE_OUT_BUF_SIZE;
496
+ m_pOut_buf = m_out_buf;
497
+
498
+ if (m_params.m_two_pass_flag)
499
+ {
500
+ clear_obj(m_huff_count);
501
+ first_pass_init();
502
+ }
503
+ else
504
+ {
505
+ memcpy(m_huff_bits[0+0], s_dc_lum_bits, 17); memcpy(m_huff_val [0+0], s_dc_lum_val, DC_LUM_CODES);
506
+ memcpy(m_huff_bits[2+0], s_ac_lum_bits, 17); memcpy(m_huff_val [2+0], s_ac_lum_val, AC_LUM_CODES);
507
+ memcpy(m_huff_bits[0+1], s_dc_chroma_bits, 17); memcpy(m_huff_val [0+1], s_dc_chroma_val, DC_CHROMA_CODES);
508
+ memcpy(m_huff_bits[2+1], s_ac_chroma_bits, 17); memcpy(m_huff_val [2+1], s_ac_chroma_val, AC_CHROMA_CODES);
509
+ if (!second_pass_init()) return false; // in effect, skip over the first pass
510
+ }
511
+ return m_all_stream_writes_succeeded;
512
+ }
513
+
514
+ void jpeg_encoder::load_block_8_8_grey(int x)
515
+ {
516
+ uint8 *pSrc;
517
+ sample_array_t *pDst = m_sample_array;
518
+ x <<= 3;
519
+ for (int i = 0; i < 8; i++, pDst += 8)
520
+ {
521
+ pSrc = m_mcu_lines[i] + x;
522
+ pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128;
523
+ pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128;
524
+ }
525
+ }
526
+
527
+ void jpeg_encoder::load_block_8_8(int x, int y, int c)
528
+ {
529
+ uint8 *pSrc;
530
+ sample_array_t *pDst = m_sample_array;
531
+ x = (x * (8 * 3)) + c;
532
+ y <<= 3;
533
+ for (int i = 0; i < 8; i++, pDst += 8)
534
+ {
535
+ pSrc = m_mcu_lines[y + i] + x;
536
+ pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128;
537
+ pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128;
538
+ }
539
+ }
540
+
541
+ void jpeg_encoder::load_block_16_8(int x, int c)
542
+ {
543
+ uint8 *pSrc1, *pSrc2;
544
+ sample_array_t *pDst = m_sample_array;
545
+ x = (x * (16 * 3)) + c;
546
+ int a = 0, b = 2;
547
+ for (int i = 0; i < 16; i += 2, pDst += 8)
548
+ {
549
+ pSrc1 = m_mcu_lines[i + 0] + x;
550
+ pSrc2 = m_mcu_lines[i + 1] + x;
551
+ pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3] + pSrc2[ 0 * 3] + pSrc2[ 1 * 3] + a) >> 2) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3] + pSrc2[ 2 * 3] + pSrc2[ 3 * 3] + b) >> 2) - 128;
552
+ pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3] + pSrc2[ 4 * 3] + pSrc2[ 5 * 3] + a) >> 2) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3] + pSrc2[ 6 * 3] + pSrc2[ 7 * 3] + b) >> 2) - 128;
553
+ pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3] + pSrc2[ 8 * 3] + pSrc2[ 9 * 3] + a) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + b) >> 2) - 128;
554
+ pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + a) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + b) >> 2) - 128;
555
+ int temp = a; a = b; b = temp;
556
+ }
557
+ }
558
+
559
+ void jpeg_encoder::load_block_16_8_8(int x, int c)
560
+ {
561
+ uint8 *pSrc1;
562
+ sample_array_t *pDst = m_sample_array;
563
+ x = (x * (16 * 3)) + c;
564
+ for (int i = 0; i < 8; i++, pDst += 8)
565
+ {
566
+ pSrc1 = m_mcu_lines[i + 0] + x;
567
+ pDst[0] = ((pSrc1[ 0 * 3] + pSrc1[ 1 * 3]) >> 1) - 128; pDst[1] = ((pSrc1[ 2 * 3] + pSrc1[ 3 * 3]) >> 1) - 128;
568
+ pDst[2] = ((pSrc1[ 4 * 3] + pSrc1[ 5 * 3]) >> 1) - 128; pDst[3] = ((pSrc1[ 6 * 3] + pSrc1[ 7 * 3]) >> 1) - 128;
569
+ pDst[4] = ((pSrc1[ 8 * 3] + pSrc1[ 9 * 3]) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3]) >> 1) - 128;
570
+ pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3]) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3]) >> 1) - 128;
571
+ }
572
+ }
573
+
574
+ void jpeg_encoder::load_quantized_coefficients(int component_num)
575
+ {
576
+ int32 *q = m_quantization_tables[component_num > 0];
577
+ int16 *pDst = m_coefficient_array;
578
+ for (int i = 0; i < 64; i++)
579
+ {
580
+ sample_array_t j = m_sample_array[s_zag[i]];
581
+ if (j < 0)
582
+ {
583
+ if ((j = -j + (*q >> 1)) < *q)
584
+ *pDst++ = 0;
585
+ else
586
+ *pDst++ = static_cast<int16>(-(j / *q));
587
+ }
588
+ else
589
+ {
590
+ if ((j = j + (*q >> 1)) < *q)
591
+ *pDst++ = 0;
592
+ else
593
+ *pDst++ = static_cast<int16>((j / *q));
594
+ }
595
+ q++;
596
+ }
597
+ }
598
+
599
+ void jpeg_encoder::flush_output_buffer()
600
+ {
601
+ if (m_out_buf_left != JPGE_OUT_BUF_SIZE)
602
+ m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left);
603
+ m_pOut_buf = m_out_buf;
604
+ m_out_buf_left = JPGE_OUT_BUF_SIZE;
605
+ }
606
+
607
+ void jpeg_encoder::put_bits(uint bits, uint len)
608
+ {
609
+ m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len)));
610
+ while (m_bits_in >= 8)
611
+ {
612
+ uint8 c;
613
+ #define JPGE_PUT_BYTE(c) { *m_pOut_buf++ = (c); if (--m_out_buf_left == 0) flush_output_buffer(); }
614
+ JPGE_PUT_BYTE(c = (uint8)((m_bit_buffer >> 16) & 0xFF));
615
+ if (c == 0xFF) JPGE_PUT_BYTE(0);
616
+ m_bit_buffer <<= 8;
617
+ m_bits_in -= 8;
618
+ }
619
+ }
620
+
621
+ void jpeg_encoder::code_coefficients_pass_one(int component_num)
622
+ {
623
+ if (component_num >= 3) return; // just to shut up static analysis
624
+ int i, run_len, nbits, temp1;
625
+ int16 *src = m_coefficient_array;
626
+ uint32 *dc_count = component_num ? m_huff_count[0 + 1] : m_huff_count[0 + 0], *ac_count = component_num ? m_huff_count[2 + 1] : m_huff_count[2 + 0];
627
+
628
+ temp1 = src[0] - m_last_dc_val[component_num];
629
+ m_last_dc_val[component_num] = src[0];
630
+ if (temp1 < 0) temp1 = -temp1;
631
+
632
+ nbits = 0;
633
+ while (temp1)
634
+ {
635
+ nbits++; temp1 >>= 1;
636
+ }
637
+
638
+ dc_count[nbits]++;
639
+ for (run_len = 0, i = 1; i < 64; i++)
640
+ {
641
+ if ((temp1 = m_coefficient_array[i]) == 0)
642
+ run_len++;
643
+ else
644
+ {
645
+ while (run_len >= 16)
646
+ {
647
+ ac_count[0xF0]++;
648
+ run_len -= 16;
649
+ }
650
+ if (temp1 < 0) temp1 = -temp1;
651
+ nbits = 1;
652
+ while (temp1 >>= 1) nbits++;
653
+ ac_count[(run_len << 4) + nbits]++;
654
+ run_len = 0;
655
+ }
656
+ }
657
+ if (run_len) ac_count[0]++;
658
+ }
659
+
660
+ void jpeg_encoder::code_coefficients_pass_two(int component_num)
661
+ {
662
+ int i, j, run_len, nbits, temp1, temp2;
663
+ int16 *pSrc = m_coefficient_array;
664
+ uint *codes[2];
665
+ uint8 *code_sizes[2];
666
+
667
+ if (component_num == 0)
668
+ {
669
+ codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0];
670
+ code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0];
671
+ }
672
+ else
673
+ {
674
+ codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1];
675
+ code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1];
676
+ }
677
+
678
+ temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num];
679
+ m_last_dc_val[component_num] = pSrc[0];
680
+
681
+ if (temp1 < 0)
682
+ {
683
+ temp1 = -temp1; temp2--;
684
+ }
685
+
686
+ nbits = 0;
687
+ while (temp1)
688
+ {
689
+ nbits++; temp1 >>= 1;
690
+ }
691
+
692
+ put_bits(codes[0][nbits], code_sizes[0][nbits]);
693
+ if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits);
694
+
695
+ for (run_len = 0, i = 1; i < 64; i++)
696
+ {
697
+ if ((temp1 = m_coefficient_array[i]) == 0)
698
+ run_len++;
699
+ else
700
+ {
701
+ while (run_len >= 16)
702
+ {
703
+ put_bits(codes[1][0xF0], code_sizes[1][0xF0]);
704
+ run_len -= 16;
705
+ }
706
+ if ((temp2 = temp1) < 0)
707
+ {
708
+ temp1 = -temp1;
709
+ temp2--;
710
+ }
711
+ nbits = 1;
712
+ while (temp1 >>= 1)
713
+ nbits++;
714
+ j = (run_len << 4) + nbits;
715
+ put_bits(codes[1][j], code_sizes[1][j]);
716
+ put_bits(temp2 & ((1 << nbits) - 1), nbits);
717
+ run_len = 0;
718
+ }
719
+ }
720
+ if (run_len)
721
+ put_bits(codes[1][0], code_sizes[1][0]);
722
+ }
723
+
724
+ void jpeg_encoder::code_block(int component_num)
725
+ {
726
+ DCT2D(m_sample_array);
727
+ load_quantized_coefficients(component_num);
728
+ if (m_pass_num == 1)
729
+ code_coefficients_pass_one(component_num);
730
+ else
731
+ code_coefficients_pass_two(component_num);
732
+ }
733
+
734
+ void jpeg_encoder::process_mcu_row()
735
+ {
736
+ if (m_num_components == 1)
737
+ {
738
+ for (int i = 0; i < m_mcus_per_row; i++)
739
+ {
740
+ load_block_8_8_grey(i); code_block(0);
741
+ }
742
+ }
743
+ else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
744
+ {
745
+ for (int i = 0; i < m_mcus_per_row; i++)
746
+ {
747
+ load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2);
748
+ }
749
+ }
750
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
751
+ {
752
+ for (int i = 0; i < m_mcus_per_row; i++)
753
+ {
754
+ load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
755
+ load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2);
756
+ }
757
+ }
758
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
759
+ {
760
+ for (int i = 0; i < m_mcus_per_row; i++)
761
+ {
762
+ load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
763
+ load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0);
764
+ load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2);
765
+ }
766
+ }
767
+ }
768
+
769
+ bool jpeg_encoder::terminate_pass_one()
770
+ {
771
+ optimize_huffman_table(0+0, DC_LUM_CODES); optimize_huffman_table(2+0, AC_LUM_CODES);
772
+ if (m_num_components > 1)
773
+ {
774
+ optimize_huffman_table(0+1, DC_CHROMA_CODES); optimize_huffman_table(2+1, AC_CHROMA_CODES);
775
+ }
776
+ return second_pass_init();
777
+ }
778
+
779
+ bool jpeg_encoder::terminate_pass_two()
780
+ {
781
+ put_bits(0x7F, 7);
782
+ flush_output_buffer();
783
+ emit_marker(M_EOI);
784
+ m_pass_num++; // purposely bump up m_pass_num, for debugging
785
+ return true;
786
+ }
787
+
788
+ bool jpeg_encoder::process_end_of_image()
789
+ {
790
+ if (m_mcu_y_ofs)
791
+ {
792
+ if (m_mcu_y_ofs < 16) // check here just to shut up static analysis
793
+ {
794
+ for (int i = m_mcu_y_ofs; i < m_mcu_y; i++)
795
+ memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu);
796
+ }
797
+
798
+ process_mcu_row();
799
+ }
800
+
801
+ if (m_pass_num == 1)
802
+ return terminate_pass_one();
803
+ else
804
+ return terminate_pass_two();
805
+ }
806
+
807
+ void jpeg_encoder::load_mcu(const void *pSrc)
808
+ {
809
+ const uint8* Psrc = reinterpret_cast<const uint8*>(pSrc);
810
+
811
+ uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst
812
+
813
+ if (m_num_components == 1)
814
+ {
815
+ if (m_image_bpp == 4)
816
+ RGBA_to_Y(pDst, Psrc, m_image_x);
817
+ else if (m_image_bpp == 3)
818
+ RGB_to_Y(pDst, Psrc, m_image_x);
819
+ else
820
+ memcpy(pDst, Psrc, m_image_x);
821
+ }
822
+ else
823
+ {
824
+ if (m_image_bpp == 4)
825
+ RGBA_to_YCC(pDst, Psrc, m_image_x);
826
+ else if (m_image_bpp == 3)
827
+ RGB_to_YCC(pDst, Psrc, m_image_x);
828
+ else
829
+ Y_to_YCC(pDst, Psrc, m_image_x);
830
+ }
831
+
832
+ // Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16
833
+ if (m_num_components == 1)
834
+ memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x);
835
+ else
836
+ {
837
+ const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2];
838
+ uint8 *q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt;
839
+ for (int i = m_image_x; i < m_image_x_mcu; i++)
840
+ {
841
+ *q++ = y; *q++ = cb; *q++ = cr;
842
+ }
843
+ }
844
+
845
+ if (++m_mcu_y_ofs == m_mcu_y)
846
+ {
847
+ process_mcu_row();
848
+ m_mcu_y_ofs = 0;
849
+ }
850
+ }
851
+
852
+ void jpeg_encoder::clear()
853
+ {
854
+ m_mcu_lines[0] = NULL;
855
+ m_pass_num = 0;
856
+ m_all_stream_writes_succeeded = true;
857
+ }
858
+
859
+ jpeg_encoder::jpeg_encoder()
860
+ {
861
+ clear();
862
+ }
863
+
864
+ jpeg_encoder::~jpeg_encoder()
865
+ {
866
+ deinit();
867
+ }
868
+
869
+ bool jpeg_encoder::init(output_stream *pStream, int64_t width, int64_t height, int64_t src_channels, const params &comp_params)
870
+ {
871
+ deinit();
872
+ if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check_valid())) return false;
873
+ m_pStream = pStream;
874
+ m_params = comp_params;
875
+ return jpg_open(width, height, src_channels);
876
+ }
877
+
878
+ void jpeg_encoder::deinit()
879
+ {
880
+ jpge_free(m_mcu_lines[0]);
881
+ clear();
882
+ }
883
+
884
+ bool jpeg_encoder::process_scanline(const void* pScanline)
885
+ {
886
+ if ((m_pass_num < 1) || (m_pass_num > 2)) return false;
887
+ if (m_all_stream_writes_succeeded)
888
+ {
889
+ if (!pScanline)
890
+ {
891
+ if (!process_end_of_image()) return false;
892
+ }
893
+ else
894
+ {
895
+ load_mcu(pScanline);
896
+ }
897
+ }
898
+ return m_all_stream_writes_succeeded;
899
+ }
900
+
901
+ // Higher level wrappers/examples (optional).
902
+ #include <stdio.h>
903
+
904
+ class cfile_stream : public output_stream
905
+ {
906
+ cfile_stream(const cfile_stream &);
907
+ cfile_stream &operator= (const cfile_stream &);
908
+
909
+ FILE* m_pFile;
910
+ bool m_bStatus;
911
+
912
+ public:
913
+ cfile_stream() : m_pFile(NULL), m_bStatus(false) { }
914
+
915
+ virtual ~cfile_stream()
916
+ {
917
+ close();
918
+ }
919
+
920
+ bool open(const char *pFilename)
921
+ {
922
+ close();
923
+ #if defined(_MSC_VER)
924
+ if (fopen_s(&m_pFile, pFilename, "wb") != 0)
925
+ {
926
+ return false;
927
+ }
928
+ #else
929
+ m_pFile = fopen(pFilename, "wb");
930
+ #endif
931
+ m_bStatus = (m_pFile != NULL);
932
+ return m_bStatus;
933
+ }
934
+
935
+ bool close()
936
+ {
937
+ if (m_pFile)
938
+ {
939
+ if (fclose(m_pFile) == EOF)
940
+ {
941
+ m_bStatus = false;
942
+ }
943
+ m_pFile = NULL;
944
+ }
945
+ return m_bStatus;
946
+ }
947
+
948
+ virtual bool put_buf(const void* pBuf, int64_t len)
949
+ {
950
+ m_bStatus = m_bStatus && (fwrite(pBuf, len, 1, m_pFile) == 1);
951
+ return m_bStatus;
952
+ }
953
+
954
+ uint get_size() const
955
+ {
956
+ return m_pFile ? ftell(m_pFile) : 0;
957
+ }
958
+ };
959
+
960
+ // Writes JPEG image to file.
961
+ bool compress_image_to_jpeg_file(const char *pFilename, int64_t width, int64_t height, int64_t num_channels, const uint8 *pImage_data, const params &comp_params)
962
+ {
963
+ cfile_stream dst_stream;
964
+ if (!dst_stream.open(pFilename))
965
+ return false;
966
+
967
+ jpge::jpeg_encoder dst_image;
968
+ if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params))
969
+ return false;
970
+
971
+ for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++)
972
+ {
973
+ for (int64_t i = 0; i < height; i++)
974
+ {
975
+ // i, width, and num_channels are all 64bit
976
+ const uint8* pBuf = pImage_data + i * width * num_channels;
977
+ if (!dst_image.process_scanline(pBuf))
978
+ return false;
979
+ }
980
+ if (!dst_image.process_scanline(NULL))
981
+ return false;
982
+ }
983
+
984
+ dst_image.deinit();
985
+
986
+ return dst_stream.close();
987
+ }
988
+
989
+ class memory_stream : public output_stream
990
+ {
991
+ memory_stream(const memory_stream &);
992
+ memory_stream &operator= (const memory_stream &);
993
+
994
+ uint8 *m_pBuf;
995
+ uint64_t m_buf_size, m_buf_ofs;
996
+
997
+ public:
998
+ memory_stream(void *pBuf, uint64_t buf_size) : m_pBuf(static_cast<uint8*>(pBuf)), m_buf_size(buf_size), m_buf_ofs(0) { }
999
+
1000
+ virtual ~memory_stream() { }
1001
+
1002
+ virtual bool put_buf(const void* pBuf, int64_t len)
1003
+ {
1004
+ uint64_t buf_remaining = m_buf_size - m_buf_ofs;
1005
+ if ((uint64_t)len > buf_remaining)
1006
+ return false;
1007
+ memcpy(m_pBuf + m_buf_ofs, pBuf, len);
1008
+ m_buf_ofs += len;
1009
+ return true;
1010
+ }
1011
+
1012
+ uint64_t get_size() const
1013
+ {
1014
+ return m_buf_ofs;
1015
+ }
1016
+ };
1017
+
1018
+ bool compress_image_to_jpeg_file_in_memory(void *pDstBuf, int64_t &buf_size, int64_t width, int64_t height, int64_t num_channels, const uint8 *pImage_data, const params &comp_params)
1019
+ {
1020
+ if ((!pDstBuf) || (!buf_size))
1021
+ return false;
1022
+
1023
+ memory_stream dst_stream(pDstBuf, buf_size);
1024
+
1025
+ buf_size = 0;
1026
+
1027
+ jpge::jpeg_encoder dst_image;
1028
+ if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params))
1029
+ return false;
1030
+
1031
+ for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++)
1032
+ {
1033
+ for (int64_t i = 0; i < height; i++)
1034
+ {
1035
+ const uint8* pScanline = pImage_data + i * width * num_channels;
1036
+ if (!dst_image.process_scanline(pScanline))
1037
+ return false;
1038
+ }
1039
+ if (!dst_image.process_scanline(NULL))
1040
+ return false;
1041
+ }
1042
+
1043
+ dst_image.deinit();
1044
+
1045
+ buf_size = dst_stream.get_size();
1046
+ return true;
1047
+ }
1048
+
1049
+ } // namespace jpge
crazy_functions/test_project/cpp/longcode/prod_cons.h ADDED
@@ -0,0 +1,433 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <atomic>
4
+ #include <utility>
5
+ #include <cstring>
6
+ #include <type_traits>
7
+ #include <cstdint>
8
+
9
+ #include "libipc/def.h"
10
+
11
+ #include "libipc/platform/detail.h"
12
+ #include "libipc/circ/elem_def.h"
13
+ #include "libipc/utility/log.h"
14
+ #include "libipc/utility/utility.h"
15
+
16
+ namespace ipc {
17
+
18
+ ////////////////////////////////////////////////////////////////
19
+ /// producer-consumer implementation
20
+ ////////////////////////////////////////////////////////////////
21
+
22
+ template <typename Flag>
23
+ struct prod_cons_impl;
24
+
25
+ template <>
26
+ struct prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
27
+
28
+ template <std::size_t DataSize, std::size_t AlignSize>
29
+ struct elem_t {
30
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
31
+ };
32
+
33
+ alignas(cache_line_size) std::atomic<circ::u2_t> rd_; // read index
34
+ alignas(cache_line_size) std::atomic<circ::u2_t> wt_; // write index
35
+
36
+ constexpr circ::u2_t cursor() const noexcept {
37
+ return 0;
38
+ }
39
+
40
+ template <typename W, typename F, typename E>
41
+ bool push(W* /*wrapper*/, F&& f, E* elems) {
42
+ auto cur_wt = circ::index_of(wt_.load(std::memory_order_relaxed));
43
+ if (cur_wt == circ::index_of(rd_.load(std::memory_order_acquire) - 1)) {
44
+ return false; // full
45
+ }
46
+ std::forward<F>(f)(&(elems[cur_wt].data_));
47
+ wt_.fetch_add(1, std::memory_order_release);
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * In single-single-unicast, 'force_push' means 'no reader' or 'the only one reader is dead'.
53
+ * So we could just disconnect all connections of receiver, and return false.
54
+ */
55
+ template <typename W, typename F, typename E>
56
+ bool force_push(W* wrapper, F&&, E*) {
57
+ wrapper->elems()->disconnect_receiver(~static_cast<circ::cc_t>(0u));
58
+ return false;
59
+ }
60
+
61
+ template <typename W, typename F, typename R, typename E>
62
+ bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, R&& out, E* elems) {
63
+ auto cur_rd = circ::index_of(rd_.load(std::memory_order_relaxed));
64
+ if (cur_rd == circ::index_of(wt_.load(std::memory_order_acquire))) {
65
+ return false; // empty
66
+ }
67
+ std::forward<F>(f)(&(elems[cur_rd].data_));
68
+ std::forward<R>(out)(true);
69
+ rd_.fetch_add(1, std::memory_order_release);
70
+ return true;
71
+ }
72
+ };
73
+
74
+ template <>
75
+ struct prod_cons_impl<wr<relat::single, relat::multi , trans::unicast>>
76
+ : prod_cons_impl<wr<relat::single, relat::single, trans::unicast>> {
77
+
78
+ template <typename W, typename F, typename E>
79
+ bool force_push(W* wrapper, F&&, E*) {
80
+ wrapper->elems()->disconnect_receiver(1);
81
+ return false;
82
+ }
83
+
84
+ template <typename W, typename F, typename R,
85
+ template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
86
+ bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, R&& out, E<DS, AS>* elems) {
87
+ byte_t buff[DS];
88
+ for (unsigned k = 0;;) {
89
+ auto cur_rd = rd_.load(std::memory_order_relaxed);
90
+ if (circ::index_of(cur_rd) ==
91
+ circ::index_of(wt_.load(std::memory_order_acquire))) {
92
+ return false; // empty
93
+ }
94
+ std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
95
+ if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
96
+ std::forward<F>(f)(buff);
97
+ std::forward<R>(out)(true);
98
+ return true;
99
+ }
100
+ ipc::yield(k);
101
+ }
102
+ }
103
+ };
104
+
105
+ template <>
106
+ struct prod_cons_impl<wr<relat::multi , relat::multi, trans::unicast>>
107
+ : prod_cons_impl<wr<relat::single, relat::multi, trans::unicast>> {
108
+
109
+ using flag_t = std::uint64_t;
110
+
111
+ template <std::size_t DataSize, std::size_t AlignSize>
112
+ struct elem_t {
113
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
114
+ std::atomic<flag_t> f_ct_ { 0 }; // commit flag
115
+ };
116
+
117
+ alignas(cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
118
+
119
+ template <typename W, typename F, typename E>
120
+ bool push(W* /*wrapper*/, F&& f, E* elems) {
121
+ circ::u2_t cur_ct, nxt_ct;
122
+ for (unsigned k = 0;;) {
123
+ cur_ct = ct_.load(std::memory_order_relaxed);
124
+ if (circ::index_of(nxt_ct = cur_ct + 1) ==
125
+ circ::index_of(rd_.load(std::memory_order_acquire))) {
126
+ return false; // full
127
+ }
128
+ if (ct_.compare_exchange_weak(cur_ct, nxt_ct, std::memory_order_acq_rel)) {
129
+ break;
130
+ }
131
+ ipc::yield(k);
132
+ }
133
+ auto* el = elems + circ::index_of(cur_ct);
134
+ std::forward<F>(f)(&(el->data_));
135
+ // set flag & try update wt
136
+ el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
137
+ while (1) {
138
+ auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
139
+ if (cur_ct != wt_.load(std::memory_order_relaxed)) {
140
+ return true;
141
+ }
142
+ if ((~cac_ct) != cur_ct) {
143
+ return true;
144
+ }
145
+ if (!el->f_ct_.compare_exchange_strong(cac_ct, 0, std::memory_order_relaxed)) {
146
+ return true;
147
+ }
148
+ wt_.store(nxt_ct, std::memory_order_release);
149
+ cur_ct = nxt_ct;
150
+ nxt_ct = cur_ct + 1;
151
+ el = elems + circ::index_of(cur_ct);
152
+ }
153
+ return true;
154
+ }
155
+
156
+ template <typename W, typename F, typename E>
157
+ bool force_push(W* wrapper, F&&, E*) {
158
+ wrapper->elems()->disconnect_receiver(1);
159
+ return false;
160
+ }
161
+
162
+ template <typename W, typename F, typename R,
163
+ template <std::size_t, std::size_t> class E, std::size_t DS, std::size_t AS>
164
+ bool pop(W* /*wrapper*/, circ::u2_t& /*cur*/, F&& f, R&& out, E<DS, AS>* elems) {
165
+ byte_t buff[DS];
166
+ for (unsigned k = 0;;) {
167
+ auto cur_rd = rd_.load(std::memory_order_relaxed);
168
+ auto cur_wt = wt_.load(std::memory_order_acquire);
169
+ auto id_rd = circ::index_of(cur_rd);
170
+ auto id_wt = circ::index_of(cur_wt);
171
+ if (id_rd == id_wt) {
172
+ auto* el = elems + id_wt;
173
+ auto cac_ct = el->f_ct_.load(std::memory_order_acquire);
174
+ if ((~cac_ct) != cur_wt) {
175
+ return false; // empty
176
+ }
177
+ if (el->f_ct_.compare_exchange_weak(cac_ct, 0, std::memory_order_relaxed)) {
178
+ wt_.store(cur_wt + 1, std::memory_order_release);
179
+ }
180
+ k = 0;
181
+ }
182
+ else {
183
+ std::memcpy(buff, &(elems[circ::index_of(cur_rd)].data_), sizeof(buff));
184
+ if (rd_.compare_exchange_weak(cur_rd, cur_rd + 1, std::memory_order_release)) {
185
+ std::forward<F>(f)(buff);
186
+ std::forward<R>(out)(true);
187
+ return true;
188
+ }
189
+ ipc::yield(k);
190
+ }
191
+ }
192
+ }
193
+ };
194
+
195
+ template <>
196
+ struct prod_cons_impl<wr<relat::single, relat::multi, trans::broadcast>> {
197
+
198
+ using rc_t = std::uint64_t;
199
+
200
+ enum : rc_t {
201
+ ep_mask = 0x00000000ffffffffull,
202
+ ep_incr = 0x0000000100000000ull
203
+ };
204
+
205
+ template <std::size_t DataSize, std::size_t AlignSize>
206
+ struct elem_t {
207
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
208
+ std::atomic<rc_t> rc_ { 0 }; // read-counter
209
+ };
210
+
211
+ alignas(cache_line_size) std::atomic<circ::u2_t> wt_; // write index
212
+ alignas(cache_line_size) rc_t epoch_ { 0 }; // only one writer
213
+
214
+ circ::u2_t cursor() const noexcept {
215
+ return wt_.load(std::memory_order_acquire);
216
+ }
217
+
218
+ template <typename W, typename F, typename E>
219
+ bool push(W* wrapper, F&& f, E* elems) {
220
+ E* el;
221
+ for (unsigned k = 0;;) {
222
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
223
+ if (cc == 0) return false; // no reader
224
+ el = elems + circ::index_of(wt_.load(std::memory_order_relaxed));
225
+ // check all consumers have finished reading this element
226
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
227
+ circ::cc_t rem_cc = cur_rc & ep_mask;
228
+ if ((cc & rem_cc) && ((cur_rc & ~ep_mask) == epoch_)) {
229
+ return false; // has not finished yet
230
+ }
231
+ // consider rem_cc to be 0 here
232
+ if (el->rc_.compare_exchange_weak(
233
+ cur_rc, epoch_ | static_cast<rc_t>(cc), std::memory_order_release)) {
234
+ break;
235
+ }
236
+ ipc::yield(k);
237
+ }
238
+ std::forward<F>(f)(&(el->data_));
239
+ wt_.fetch_add(1, std::memory_order_release);
240
+ return true;
241
+ }
242
+
243
+ template <typename W, typename F, typename E>
244
+ bool force_push(W* wrapper, F&& f, E* elems) {
245
+ E* el;
246
+ epoch_ += ep_incr;
247
+ for (unsigned k = 0;;) {
248
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
249
+ if (cc == 0) return false; // no reader
250
+ el = elems + circ::index_of(wt_.load(std::memory_order_relaxed));
251
+ // check all consumers have finished reading this element
252
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
253
+ circ::cc_t rem_cc = cur_rc & ep_mask;
254
+ if (cc & rem_cc) {
255
+ ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
256
+ cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
257
+ if (cc == 0) return false; // no reader
258
+ }
259
+ // just compare & exchange
260
+ if (el->rc_.compare_exchange_weak(
261
+ cur_rc, epoch_ | static_cast<rc_t>(cc), std::memory_order_release)) {
262
+ break;
263
+ }
264
+ ipc::yield(k);
265
+ }
266
+ std::forward<F>(f)(&(el->data_));
267
+ wt_.fetch_add(1, std::memory_order_release);
268
+ return true;
269
+ }
270
+
271
+ template <typename W, typename F, typename R, typename E>
272
+ bool pop(W* wrapper, circ::u2_t& cur, F&& f, R&& out, E* elems) {
273
+ if (cur == cursor()) return false; // acquire
274
+ auto* el = elems + circ::index_of(cur++);
275
+ std::forward<F>(f)(&(el->data_));
276
+ for (unsigned k = 0;;) {
277
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
278
+ if ((cur_rc & ep_mask) == 0) {
279
+ std::forward<R>(out)(true);
280
+ return true;
281
+ }
282
+ auto nxt_rc = cur_rc & ~static_cast<rc_t>(wrapper->connected_id());
283
+ if (el->rc_.compare_exchange_weak(cur_rc, nxt_rc, std::memory_order_release)) {
284
+ std::forward<R>(out)((nxt_rc & ep_mask) == 0);
285
+ return true;
286
+ }
287
+ ipc::yield(k);
288
+ }
289
+ }
290
+ };
291
+
292
+ template <>
293
+ struct prod_cons_impl<wr<relat::multi, relat::multi, trans::broadcast>> {
294
+
295
+ using rc_t = std::uint64_t;
296
+ using flag_t = std::uint64_t;
297
+
298
+ enum : rc_t {
299
+ rc_mask = 0x00000000ffffffffull,
300
+ ep_mask = 0x00ffffffffffffffull,
301
+ ep_incr = 0x0100000000000000ull,
302
+ ic_mask = 0xff000000ffffffffull,
303
+ ic_incr = 0x0000000100000000ull
304
+ };
305
+
306
+ template <std::size_t DataSize, std::size_t AlignSize>
307
+ struct elem_t {
308
+ std::aligned_storage_t<DataSize, AlignSize> data_ {};
309
+ std::atomic<rc_t > rc_ { 0 }; // read-counter
310
+ std::atomic<flag_t> f_ct_ { 0 }; // commit flag
311
+ };
312
+
313
+ alignas(cache_line_size) std::atomic<circ::u2_t> ct_; // commit index
314
+ alignas(cache_line_size) std::atomic<rc_t> epoch_ { 0 };
315
+
316
+ circ::u2_t cursor() const noexcept {
317
+ return ct_.load(std::memory_order_acquire);
318
+ }
319
+
320
+ constexpr static rc_t inc_rc(rc_t rc) noexcept {
321
+ return (rc & ic_mask) | ((rc + ic_incr) & ~ic_mask);
322
+ }
323
+
324
+ constexpr static rc_t inc_mask(rc_t rc) noexcept {
325
+ return inc_rc(rc) & ~rc_mask;
326
+ }
327
+
328
+ template <typename W, typename F, typename E>
329
+ bool push(W* wrapper, F&& f, E* elems) {
330
+ E* el;
331
+ circ::u2_t cur_ct;
332
+ rc_t epoch = epoch_.load(std::memory_order_acquire);
333
+ for (unsigned k = 0;;) {
334
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
335
+ if (cc == 0) return false; // no reader
336
+ el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
337
+ // check all consumers have finished reading this element
338
+ auto cur_rc = el->rc_.load(std::memory_order_relaxed);
339
+ circ::cc_t rem_cc = cur_rc & rc_mask;
340
+ if ((cc & rem_cc) && ((cur_rc & ~ep_mask) == epoch)) {
341
+ return false; // has not finished yet
342
+ }
343
+ else if (!rem_cc) {
344
+ auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
345
+ if ((cur_fl != cur_ct) && cur_fl) {
346
+ return false; // full
347
+ }
348
+ }
349
+ // consider rem_cc to be 0 here
350
+ if (el->rc_.compare_exchange_weak(
351
+ cur_rc, inc_mask(epoch | (cur_rc & ep_mask)) | static_cast<rc_t>(cc), std::memory_order_relaxed) &&
352
+ epoch_.compare_exchange_weak(epoch, epoch, std::memory_order_acq_rel)) {
353
+ break;
354
+ }
355
+ ipc::yield(k);
356
+ }
357
+ // only one thread/process would touch here at one time
358
+ ct_.store(cur_ct + 1, std::memory_order_release);
359
+ std::forward<F>(f)(&(el->data_));
360
+ // set flag & try update wt
361
+ el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
362
+ return true;
363
+ }
364
+
365
+ template <typename W, typename F, typename E>
366
+ bool force_push(W* wrapper, F&& f, E* elems) {
367
+ E* el;
368
+ circ::u2_t cur_ct;
369
+ rc_t epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
370
+ for (unsigned k = 0;;) {
371
+ circ::cc_t cc = wrapper->elems()->connections(std::memory_order_relaxed);
372
+ if (cc == 0) return false; // no reader
373
+ el = elems + circ::index_of(cur_ct = ct_.load(std::memory_order_relaxed));
374
+ // check all consumers have finished reading this element
375
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
376
+ circ::cc_t rem_cc = cur_rc & rc_mask;
377
+ if (cc & rem_cc) {
378
+ ipc::log("force_push: k = %u, cc = %u, rem_cc = %u\n", k, cc, rem_cc);
379
+ cc = wrapper->elems()->disconnect_receiver(rem_cc); // disconnect all invalid readers
380
+ if (cc == 0) return false; // no reader
381
+ }
382
+ // just compare & exchange
383
+ if (el->rc_.compare_exchange_weak(
384
+ cur_rc, inc_mask(epoch | (cur_rc & ep_mask)) | static_cast<rc_t>(cc), std::memory_order_relaxed)) {
385
+ if (epoch == epoch_.load(std::memory_order_acquire)) {
386
+ break;
387
+ }
388
+ else if (push(wrapper, std::forward<F>(f), elems)) {
389
+ return true;
390
+ }
391
+ epoch = epoch_.fetch_add(ep_incr, std::memory_order_release) + ep_incr;
392
+ }
393
+ ipc::yield(k);
394
+ }
395
+ // only one thread/process would touch here at one time
396
+ ct_.store(cur_ct + 1, std::memory_order_release);
397
+ std::forward<F>(f)(&(el->data_));
398
+ // set flag & try update wt
399
+ el->f_ct_.store(~static_cast<flag_t>(cur_ct), std::memory_order_release);
400
+ return true;
401
+ }
402
+
403
+ template <typename W, typename F, typename R, typename E, std::size_t N>
404
+ bool pop(W* wrapper, circ::u2_t& cur, F&& f, R&& out, E(& elems)[N]) {
405
+ auto* el = elems + circ::index_of(cur);
406
+ auto cur_fl = el->f_ct_.load(std::memory_order_acquire);
407
+ if (cur_fl != ~static_cast<flag_t>(cur)) {
408
+ return false; // empty
409
+ }
410
+ ++cur;
411
+ std::forward<F>(f)(&(el->data_));
412
+ for (unsigned k = 0;;) {
413
+ auto cur_rc = el->rc_.load(std::memory_order_acquire);
414
+ if ((cur_rc & rc_mask) == 0) {
415
+ std::forward<R>(out)(true);
416
+ el->f_ct_.store(cur + N - 1, std::memory_order_release);
417
+ return true;
418
+ }
419
+ auto nxt_rc = inc_rc(cur_rc) & ~static_cast<rc_t>(wrapper->connected_id());
420
+ bool last_one = false;
421
+ if ((last_one = (nxt_rc & rc_mask) == 0)) {
422
+ el->f_ct_.store(cur + N - 1, std::memory_order_release);
423
+ }
424
+ if (el->rc_.compare_exchange_weak(cur_rc, nxt_rc, std::memory_order_release)) {
425
+ std::forward<R>(out)(last_one);
426
+ return true;
427
+ }
428
+ ipc::yield(k);
429
+ }
430
+ }
431
+ };
432
+
433
+ } // namespace ipc