Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
4f88a475a2 | |||
8759d58d39 | |||
3ddd60d8c5 | |||
326ccb779f | |||
7bf3470bf7 | |||
6776c17df3 | |||
983af1ad09 | |||
edf95885ae | |||
5e1678604b | |||
57c550a693 |
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.hugo_build.lock
|
||||||
|
public
|
30
README.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Обновленный TheDroth.Rocks
|
||||||
|
## Подготовка
|
||||||
|
- Установить Docker
|
||||||
|
- Загрузить образ с [Hugo](https://hub.docker.com/r/klakegg/hugo):
|
||||||
|
```
|
||||||
|
docker pull klakegg/hugo
|
||||||
|
```
|
||||||
|
## Запуск
|
||||||
|
- Перейти в корневой каталог с проектом
|
||||||
|
- Выполнить запуск Hugo сервера
|
||||||
|
1) Для тестов:
|
||||||
|
```
|
||||||
|
docker run --rm -v $(pwd):/src -p 1313:1313 klakegg/hugo server
|
||||||
|
```
|
||||||
|
2) Для прода:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -v $(pwd):/src -p 1313:1313 klakegg/hugo
|
||||||
|
```
|
||||||
|
## Мини FAQ
|
||||||
|
- ```config.yml``` - основной файл настройки сайта.
|
||||||
|
- Записи (странички) складываются в ```content/core``` и имеют ```.md``` формат.
|
||||||
|
- Если нужно, чтобы страницы не отображались на главном экране - расположите их в директории отличной от ```content/core```. Например, для блога можно использовать ```content/blog```.
|
||||||
|
- Картинки лежат в ```static```.
|
||||||
|
|
||||||
|
## Документация
|
||||||
|
- [Hugo Docker](https://hub.docker.com/r/klakegg/hugo)
|
||||||
|
- [Hugo Docs](https://gohugo.io/documentation/)
|
||||||
|
- [Template Repo](https://github.com/adityatelange/hugo-PaperMod)
|
||||||
|
- [Template miniFAQ](https://adityatelange.github.io/hugo-PaperMod/posts/papermod/papermod-faq/)
|
6
archetypes/default.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title: "{{ replace .Name "-" " " | title }}"
|
||||||
|
date: {{ .Date }}
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
3
baseconfig.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
baseURL = 'http://example.org/'
|
||||||
|
languageCode = 'en-us'
|
||||||
|
title = 'My New Hugo Site'
|
146
config.yml
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
baseURL: "https://thedroth.rocks"
|
||||||
|
title: TheDrothRocks!
|
||||||
|
paginate: 5
|
||||||
|
theme: PaperMod
|
||||||
|
|
||||||
|
enableRobotsTXT: true
|
||||||
|
buildDrafts: false
|
||||||
|
buildFuture: false
|
||||||
|
buildExpired: false
|
||||||
|
|
||||||
|
# googleAnalytics: UA-123-45
|
||||||
|
|
||||||
|
minify:
|
||||||
|
disableXML: true
|
||||||
|
minifyOutput: true
|
||||||
|
|
||||||
|
params:
|
||||||
|
env: production # to enable google analytics, opengraph, twitter-cards and schema.
|
||||||
|
title: TheDroth Rocks!
|
||||||
|
description: "TheДроты рулят"
|
||||||
|
keywords: [Blog, Portfolio, PaperMod]
|
||||||
|
author: ["nothing", "sn4il"]
|
||||||
|
# author: ["Me", "You"] # multiple authors
|
||||||
|
images: ["<link or path of image for opengraph, twitter-cards>"]
|
||||||
|
DateFormat: "January 2, 2006"
|
||||||
|
defaultTheme: auto # dark, light
|
||||||
|
disableThemeToggle: false
|
||||||
|
|
||||||
|
ShowReadingTime: true
|
||||||
|
ShowShareButtons: true
|
||||||
|
ShowPostNavLinks: true
|
||||||
|
ShowBreadCrumbs: true
|
||||||
|
ShowCodeCopyButtons: false
|
||||||
|
ShowWordCount: true
|
||||||
|
ShowRssButtonInSectionTermList: true
|
||||||
|
UseHugoToc: true
|
||||||
|
disableSpecial1stPost: false
|
||||||
|
disableScrollToTop: false
|
||||||
|
comments: false
|
||||||
|
hidemeta: false
|
||||||
|
hideSummary: false
|
||||||
|
showtoc: false
|
||||||
|
tocopen: false
|
||||||
|
params:
|
||||||
|
# mainSections:
|
||||||
|
# - core
|
||||||
|
# - blog
|
||||||
|
|
||||||
|
|
||||||
|
assets:
|
||||||
|
# disableHLJS: true # to disable highlight.js
|
||||||
|
# disableFingerprinting: true
|
||||||
|
favicon: "/images/favicon.png"
|
||||||
|
favicon16x16: "/images/favicon.png"
|
||||||
|
favicon32x32: "/images/favicon.png"
|
||||||
|
apple_touch_icon: "<link / abs url>"
|
||||||
|
safari_pinned_tab: "<link / abs url>"
|
||||||
|
|
||||||
|
label:
|
||||||
|
text: "TheDroth.Rocks"
|
||||||
|
icon: "/images/thedroth_logo.png"
|
||||||
|
iconHeight: 35
|
||||||
|
|
||||||
|
# profile-mode
|
||||||
|
profileMode:
|
||||||
|
enabled: false # needs to be explicitly set
|
||||||
|
title: ExampleSite
|
||||||
|
subtitle: "This is subtitle"
|
||||||
|
imageUrl: "<img location>"
|
||||||
|
imageWidth: 120
|
||||||
|
imageHeight: 120
|
||||||
|
imageTitle: my image
|
||||||
|
buttons:
|
||||||
|
- name: Posts
|
||||||
|
url: posts
|
||||||
|
- name: Tags
|
||||||
|
url: tags
|
||||||
|
|
||||||
|
# home-info mode
|
||||||
|
homeInfoParams:
|
||||||
|
Title: "Привет всем TheDroth'ам \U0001F44B"
|
||||||
|
Content: Добро пожаловать на наш сайт!
|
||||||
|
|
||||||
|
socialIcons:
|
||||||
|
- name: gitea
|
||||||
|
url: "https://git.thedroth.rocks/TheDroth/thedroth-rocks"
|
||||||
|
- name: piped
|
||||||
|
url: "https://piped.thedroth.rocks/"
|
||||||
|
|
||||||
|
|
||||||
|
analytics:
|
||||||
|
google:
|
||||||
|
SiteVerificationTag: "XYZabc"
|
||||||
|
bing:
|
||||||
|
SiteVerificationTag: "XYZabc"
|
||||||
|
yandex:
|
||||||
|
SiteVerificationTag: "XYZabc"
|
||||||
|
|
||||||
|
cover:
|
||||||
|
hidden: true # hide everywhere but not in structured data
|
||||||
|
hiddenInList: true # hide on list pages and home
|
||||||
|
hiddenInSingle: true # hide on single page
|
||||||
|
|
||||||
|
# editPost:
|
||||||
|
# URL: "https://github.com/<path_to_repo>/content"
|
||||||
|
# Text: "Suggest Changes" # edit text
|
||||||
|
# appendFilePath: true # to append file path to Edit link
|
||||||
|
|
||||||
|
# for search
|
||||||
|
# https://fusejs.io/api/options.html
|
||||||
|
fuseOpts:
|
||||||
|
isCaseSensitive: false
|
||||||
|
shouldSort: true
|
||||||
|
location: 0
|
||||||
|
distance: 1000
|
||||||
|
threshold: 0.4
|
||||||
|
minMatchCharLength: 0
|
||||||
|
keys: ["title", "permalink", "summary", "content"]
|
||||||
|
menu:
|
||||||
|
main:
|
||||||
|
- identifier: our-services
|
||||||
|
name: Наши сервисы
|
||||||
|
url: /core/our-services/
|
||||||
|
weight: 10
|
||||||
|
- identifier: proxy
|
||||||
|
name: Проксирующие сервисы
|
||||||
|
url: /core/proxy/
|
||||||
|
weight: 20
|
||||||
|
- identifier: tos
|
||||||
|
name: ToS
|
||||||
|
url: https://git.thedroth.rocks/TheDroth/ToS/src/branch/main/README.md
|
||||||
|
weight: 30
|
||||||
|
- identifier: blog
|
||||||
|
name: Блог
|
||||||
|
url: /tags/blog/
|
||||||
|
weight: 40
|
||||||
|
# Read: https://github.com/adityatelange/hugo-PaperMod/wiki/FAQs#using-hugos-syntax-highlighter-chroma
|
||||||
|
pygmentsUseClasses: true
|
||||||
|
markup:
|
||||||
|
highlight:
|
||||||
|
noClasses: false
|
||||||
|
# anchorLineNos: true
|
||||||
|
# codeFences: true
|
||||||
|
# guessSyntax: true
|
||||||
|
# lineNos: true
|
||||||
|
# style: monokai
|
4
content/blog/\
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Закрытие Asocial
|
||||||
|
|
||||||
|
Сервис Lemmy выключен — мало кем используется в силу своей нишевости, а ресурсов потреблял будь здоров.
|
||||||
|
Думаем, что открыть на замену.
|
28
content/blog/lemmy.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
title: "Закрытие Asocial"
|
||||||
|
date: 2023-12-12T15:45:03+03:00
|
||||||
|
weight: 2
|
||||||
|
tags: ["blog"]
|
||||||
|
author: "sn4il"
|
||||||
|
showToc: false
|
||||||
|
TocOpen: false
|
||||||
|
draft: false
|
||||||
|
hidemeta: false
|
||||||
|
comments: false
|
||||||
|
disableHLJS: true # to disable highlightjs
|
||||||
|
disableShare: false
|
||||||
|
disableHLJS: false
|
||||||
|
hideSummary: false
|
||||||
|
searchHidden: true
|
||||||
|
ShowReadingTime: false
|
||||||
|
ShowBreadCrumbs: true
|
||||||
|
ShowPostNavLinks: true
|
||||||
|
ShowWordCount: false
|
||||||
|
ShowRssButtonInSectionTermList: true
|
||||||
|
UseHugoToc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Закрытие Asocial
|
||||||
|
|
||||||
|
Сервис Lemmy выключен — мало кем используется в силу своей нишевости, а ресурсов потреблял будь здоров.
|
||||||
|
Думаем, что открыть на замену.
|
27
content/blog/redesign.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
title: "Редизайн"
|
||||||
|
date: 2023-10-26T08:30:03+03:00
|
||||||
|
weight: 2
|
||||||
|
tags: ["blog"]
|
||||||
|
author: "sn4il"
|
||||||
|
showToc: false
|
||||||
|
TocOpen: false
|
||||||
|
draft: false
|
||||||
|
hidemeta: false
|
||||||
|
comments: false
|
||||||
|
disableHLJS: true # to disable highlightjs
|
||||||
|
disableShare: false
|
||||||
|
disableHLJS: false
|
||||||
|
hideSummary: false
|
||||||
|
searchHidden: true
|
||||||
|
ShowReadingTime: false
|
||||||
|
ShowBreadCrumbs: true
|
||||||
|
ShowPostNavLinks: true
|
||||||
|
ShowWordCount: false
|
||||||
|
ShowRssButtonInSectionTermList: true
|
||||||
|
UseHugoToc: true
|
||||||
|
---
|
||||||
|
|
||||||
|
# Смена дизайна
|
||||||
|
|
||||||
|
Благодаря усилиям товарища Nothing, сайт переведён с олдскульного статического HTML на Hugo. Теперь мы не только симпатично смотримся, но и можем вести блог. Здесь будут публиковаться изменения и планы касательно сервисов и дальнейшего развития проекта. И, возможно, внутренние новости от команды.
|
37
content/core/donate.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "Donate!"
|
||||||
|
date: 2023-10-26T09:00:03+03:00
|
||||||
|
weight: 3
|
||||||
|
# aliases: ["/first"]
|
||||||
|
#tags: ["donae"]
|
||||||
|
author: "sn4il"
|
||||||
|
# author: ["Me", "You"] # multiple authors
|
||||||
|
showToc: true
|
||||||
|
TocOpen: false
|
||||||
|
draft: false
|
||||||
|
hidemeta: false
|
||||||
|
comments: false
|
||||||
|
description: "Поддержка - это хорошо, а материальная поддержка - всегда спасибо!"
|
||||||
|
disableHLJS: true # to disable highlightjs
|
||||||
|
disableShare: false
|
||||||
|
disableHLJS: false
|
||||||
|
hideSummary: false
|
||||||
|
searchHidden: true
|
||||||
|
ShowReadingTime: false
|
||||||
|
ShowBreadCrumbs: true
|
||||||
|
ShowPostNavLinks: true
|
||||||
|
ShowWordCount: false
|
||||||
|
ShowRssButtonInSectionTermList: true
|
||||||
|
UseHugoToc: true
|
||||||
|
---
|
||||||
|
Больше спасибо, что посетили данную страницу! Обращаем ваше внимание, что материальная поддержка вовсе не обязательна и вы можете пользоваться всеми нашими ресурсами абсолютно бесплатно!
|
||||||
|
## Способы поддержки
|
||||||
|
|
||||||
|
|
||||||
|
- [CloudTips](https://pay.cloudtips.ru/p/f13dbdef)
|
||||||
|
|
||||||
|
- Криптовалюта:
|
||||||
|
- btc:
|
||||||
|
```
|
||||||
|
bc1qmfrmydd2ck54tka9uyens3wfkhl36tlrn7ls3t
|
||||||
|
```
|
46
content/core/our-services.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
title: "Наши сервисы"
|
||||||
|
date: 2023-10-26T09:00:03+03:00
|
||||||
|
weight: 1
|
||||||
|
# aliases: ["/first"]
|
||||||
|
tags: ["first"]
|
||||||
|
# author: "nothing"
|
||||||
|
author: ["nothing", "sn4il"] # multiple authors
|
||||||
|
showToc: true
|
||||||
|
TocOpen: false
|
||||||
|
draft: false
|
||||||
|
hidemeta: false
|
||||||
|
comments: false
|
||||||
|
description: "Наши собственные сервисы."
|
||||||
|
disableHLJS: true # to disable highlightjs
|
||||||
|
disableShare: false
|
||||||
|
disableHLJS: false
|
||||||
|
hideSummary: false
|
||||||
|
searchHidden: true
|
||||||
|
ShowReadingTime: true
|
||||||
|
ShowBreadCrumbs: true
|
||||||
|
ShowPostNavLinks: true
|
||||||
|
ShowWordCount: true
|
||||||
|
ShowRssButtonInSectionTermList: true
|
||||||
|
UseHugoToc: true
|
||||||
|
---
|
||||||
|
Список сервисов, расположенных на серверах TheDroth. Все сервисы из данного списка "подчиняются" нашему [ToS](https://git.thedroth.rocks/TheDroth/ToS/src/branch/main/README.md), а потому настоятельно рекомендуем ознакомиться с ним перед началом взаимодействия с ресурсами.
|
||||||
|
|
||||||
|
## [TheДротский поиск SearXNG](https://search.thedroth.rocks/)
|
||||||
|
|
||||||
|
[![](../../images/searx.png)](https://search.thedroth.rocks/)
|
||||||
|
|
||||||
|
Метапоисковый сервис (поиск через Google, Bing, DuckDuckGo...)
|
||||||
|
|
||||||
|
## [Gitea](https://git.thedroth.rocks)
|
||||||
|
|
||||||
|
[![](../../images/git.png)](https://git.thedroth.rocks/)
|
||||||
|
|
||||||
|
Хранилище исходных кодов
|
||||||
|
|
||||||
|
## [RSS Bridge](https://rss.thedroth.rocks/)
|
||||||
|
|
||||||
|
[![](../../images/rss.png)](https://rss.thedroth.rocks/)
|
||||||
|
|
||||||
|
Конвертация новостных лент в RSS
|
||||||
|
|
40
content/core/proxy.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
title: "Проксирующие сервисы"
|
||||||
|
date: 2023-10-26T09:00:03+03:00
|
||||||
|
weight: 2
|
||||||
|
# aliases: ["/first"]
|
||||||
|
#tags: ["first"]
|
||||||
|
#author: "nothing"
|
||||||
|
author: ["nothing", "sn4il"] # multiple authors
|
||||||
|
showToc: false
|
||||||
|
TocOpen: false
|
||||||
|
draft: false
|
||||||
|
hidemeta: false
|
||||||
|
comments: false
|
||||||
|
description: "Все сервисы, которые мы проксируем."
|
||||||
|
disableHLJS: true # to disable highlightjs
|
||||||
|
disableShare: false
|
||||||
|
disableHLJS: false
|
||||||
|
hideSummary: false
|
||||||
|
searchHidden: true
|
||||||
|
ShowReadingTime: false
|
||||||
|
ShowBreadCrumbs: true
|
||||||
|
ShowPostNavLinks: true
|
||||||
|
ShowWordCount: false
|
||||||
|
ShowRssButtonInSectionTermList: true
|
||||||
|
UseHugoToc: true
|
||||||
|
---
|
||||||
|
Список прокси — альтернативных фронтэндов для централизованных сервисов (Youtube, Wikipedia...), облегчающих взаимодействие с последними.
|
||||||
|
|
||||||
|
|
||||||
|
## [Piped](https://piped.thedroth.rocks/)
|
||||||
|
|
||||||
|
[![](../../images/piped.png)](https://piped.thedroth.rocks/)
|
||||||
|
|
||||||
|
Лёгкий приватный интерфейс для YouTube, умеюший автоматически проматывать рекламные вставки. Зачем кормить гугл, если можно не кормить...
|
||||||
|
|
||||||
|
## [WikiLess](https://wiki.thedroth.rocks/)
|
||||||
|
|
||||||
|
[![](../../images/wikiless.png)](https://wiki.thedroth.rocks)
|
||||||
|
|
||||||
|
Wikipedia без ненужных элементов и трекеров
|
26
content/example.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
title: "Наши сервисы" # Название материала
|
||||||
|
date: 2020-09-15T11:30:03+00:00 # Дата создания материала
|
||||||
|
weight: 1 # "Важность материала. Чем ниже 'важность' - тем выше в списке будет находится"
|
||||||
|
# aliases: ["/first"] #
|
||||||
|
tags: ["first"] # теги. Необходимо материалы помечать по тегам для того, чтобы проще было находить нужный матриеал
|
||||||
|
author: "nothing" # один автор материала
|
||||||
|
author: ["nothing", "sn4il"] # много авторов материала
|
||||||
|
showToc: true # оглавление загаловков в материале. True - показывать заголовки в материале
|
||||||
|
TocOpen: true # оглавление загаловков в материале. True - показывать заголовки в материале
|
||||||
|
draft: true # "черновик". True - материал виден публично и считается опубликованным. False - его не видно.
|
||||||
|
hidemeta: false #
|
||||||
|
comments: false # Комментарии
|
||||||
|
description: "Наши собственные сервисы. Заголовки - ссылки." # Небольшая аннотация под заголовком
|
||||||
|
disableHLJS: true # to disable highlightjs
|
||||||
|
disableShare: false # иконки "поделиться" внизу материала. True - иконок не будет.
|
||||||
|
disableHLJS: false #
|
||||||
|
hideSummary: true # Описание материала до входа на сам материал. True - скрыть описание, оставить только заголовок.
|
||||||
|
searchHidden: true #
|
||||||
|
ShowReadingTime: true # Приблизительное время на чтение. True - показывать.
|
||||||
|
ShowBreadCrumbs: true #
|
||||||
|
ShowPostNavLinks: true # Кнопки "другой материал" внизу материала. True - показывать кнопки.
|
||||||
|
ShowWordCount: true # Количество слов в материале. True - показывать кол-во слов.
|
||||||
|
ShowRssButtonInSectionTermList: true #
|
||||||
|
UseHugoToc: false #
|
||||||
|
---
|
1
css/picnic.min.css
vendored
186
dither.py
@ -1,186 +0,0 @@
|
|||||||
#!/usr/local/bin/python3.8
|
|
||||||
|
|
||||||
import hitherdither
|
|
||||||
import os
|
|
||||||
import argparse
|
|
||||||
import shutil
|
|
||||||
from PIL import Image
|
|
||||||
import logging
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
"""
|
|
||||||
This script recursively traverses folders and creates dithered versions of the images it finds.
|
|
||||||
These are stored in the same folder as the images in a folder called "dithers".
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-d', '--directory', help="Set the directory to traverse", default="."
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-rm', '--remove', help="Removes all the folders with dithers and their contents", action="store_true"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-c', '--colorize', help="Colorizes the dithered images", action="store_true"
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'-v', '--verbose', help="Print out more detailed information about what this script is doing", action="store_true"
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
image_ext = [".jpg", ".JPG", ".jpeg", ".png", ".gif", ".webp", ".tiff", ".bmp"]
|
|
||||||
|
|
||||||
|
|
||||||
content_dir = args.directory
|
|
||||||
|
|
||||||
if args.verbose:
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
else:
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
|
||||||
|
|
||||||
exclude_dirs = set(["dithers"])
|
|
||||||
|
|
||||||
|
|
||||||
logging.info("Dithering all images in {} and subfolders".format(content_dir))
|
|
||||||
logging.debug("excluding directories: {}".format("".join(exclude_dirs)))
|
|
||||||
|
|
||||||
def colorize(source_image, category):
|
|
||||||
"""
|
|
||||||
Picks a colored dithering palette based on the post category.
|
|
||||||
"""
|
|
||||||
|
|
||||||
colors = {
|
|
||||||
'low-tech': hitherdither.palette.Palette([(30,32,40), (11,21,71),(57,77,174),(158,168,218),(187,196,230),(243,244,250)]),
|
|
||||||
'obsolete': hitherdither.palette.Palette([(9,74,58), (58,136,118),(101,163,148),(144,189,179),(169,204,195),(242,247,246)]),
|
|
||||||
'high-tech': hitherdither.palette.Palette([(86,9,6), (197,49,45),(228,130,124),(233,155,151),(242,193,190),(252,241,240)]),
|
|
||||||
'grayscale': hitherdither.palette.Palette([(25,25,25), (75,75,75),(125,125,125),(175,175,175),(225,225,225),(250,250,250)])
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if category:
|
|
||||||
|
|
||||||
for i in colors.keys():
|
|
||||||
if i in category.lower():
|
|
||||||
color = colors[i]
|
|
||||||
logging.info("Applying color palette '{}' for {}".format(i, category))
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
logging.info("No category for {}, {}".format(source_image, category))
|
|
||||||
print("No category for {}, {}".format(source_image, category))
|
|
||||||
color = colors['grayscale']
|
|
||||||
|
|
||||||
else:
|
|
||||||
logging.info("No category for {}, {}".format(source_image, category))
|
|
||||||
print("No category for {}, {}".format(source_image, category))
|
|
||||||
color = colors['grayscale']
|
|
||||||
|
|
||||||
return color
|
|
||||||
|
|
||||||
|
|
||||||
def dither_image(source_image, output_image, category ='high-tech'):
|
|
||||||
#see hitherdither docs for different dithering algos and settings
|
|
||||||
|
|
||||||
# if args.colorize:
|
|
||||||
# palette = colorize(source_image, category)
|
|
||||||
# else:
|
|
||||||
# palette = hitherdither.palette.Palette([(25,25,25), (75,75,75),(125,125,125),(175,175,175),(225,225,225),(250,250,250)])
|
|
||||||
try:
|
|
||||||
img= Image.open(source_image).convert('RGB')
|
|
||||||
img.thumbnail((800,800), Image.LANCZOS)
|
|
||||||
#palette = palettes[category]
|
|
||||||
#palette = hitherdither.palette.Palette.create_by_median_cut(img)
|
|
||||||
palette = hitherdither.palette.Palette(
|
|
||||||
[0x080000, 0x201A0B, 0x432817, 0x492910,
|
|
||||||
0x234309, 0x5D4F1E, 0x9C6B20, 0xA9220F,
|
|
||||||
0x2B347C, 0x2B7409, 0xD0CA40, 0xE8A077,
|
|
||||||
0x6A94AB, 0xD5C4B3, 0xFCE76E, 0xFCFAE2]
|
|
||||||
)
|
|
||||||
threshold = [96, 96, 96]
|
|
||||||
img_dithered = hitherdither.ordered.bayer.bayer_dithering(img, palette, threshold, order=8)
|
|
||||||
#if args.colorize:
|
|
||||||
# img_dithered = colorize(img_dithered, category)
|
|
||||||
# logging.debug("Created {} in category {}".format(img_dithered, category))
|
|
||||||
|
|
||||||
img_dithered.save(output_image, optimize=True)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logging.debug(" failed to convert {}".format(source_image))
|
|
||||||
logging.debug(e)
|
|
||||||
|
|
||||||
def delete_dithers(content_dir):
|
|
||||||
logging.info("Deleting 'dither' folders in {} and below".format(content_dir))
|
|
||||||
for root, dirs, files in os.walk(content_dir, topdown=True):
|
|
||||||
if root.endswith('dithers'):
|
|
||||||
shutil.rmtree(root)
|
|
||||||
logging.info("Removed {}".format(root))
|
|
||||||
|
|
||||||
|
|
||||||
def parse_front_matter(md):
|
|
||||||
with open(md) as f:
|
|
||||||
contents = f.readlines()
|
|
||||||
cat = None
|
|
||||||
for l in contents:
|
|
||||||
if l.startswith("categories: "):
|
|
||||||
cat = l.split("categories: ")[1]
|
|
||||||
cat = cat.strip("[")
|
|
||||||
cat = cat.strip()
|
|
||||||
cat = cat.strip("]")
|
|
||||||
|
|
||||||
logging.debug("Categories: {} from {}".format(cat, l.strip()))
|
|
||||||
return cat
|
|
||||||
|
|
||||||
prev_root = None
|
|
||||||
|
|
||||||
if args.remove:
|
|
||||||
delete_dithers(
|
|
||||||
os.path.abspath(content_dir)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for root, dirs, files in os.walk(os.path.abspath(content_dir), topdown=True):
|
|
||||||
logging.debug("Checking next folder {}".format(root))
|
|
||||||
|
|
||||||
dirs[:] = [d for d in dirs if d not in exclude_dirs]
|
|
||||||
|
|
||||||
category = None
|
|
||||||
if prev_root is None:
|
|
||||||
prev_root = root
|
|
||||||
|
|
||||||
if prev_root is not root:
|
|
||||||
if files:
|
|
||||||
if any(x.endswith(tuple(image_ext)) for x in files):
|
|
||||||
if not os.path.exists(os.path.join(root,'dithers')):
|
|
||||||
os.mkdir(os.path.join(root,'dithers'))
|
|
||||||
logging.info(" created in {}".format(root))
|
|
||||||
|
|
||||||
if args.colorize:
|
|
||||||
#iterate over md files to find one with a category
|
|
||||||
if not category:
|
|
||||||
for i in os.listdir(root):
|
|
||||||
if i.startswith('index'):
|
|
||||||
category2 = parse_front_matter(os.path.join(root,i))
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
for fname in files:
|
|
||||||
if fname.endswith(tuple(image_ext)):
|
|
||||||
file_, ext = os.path.splitext(fname)
|
|
||||||
source_image= os.path.join(root,fname)
|
|
||||||
output_image = os.path.join(os.path.join(root, 'dithers'), file_+'_dithered.png')
|
|
||||||
if not os.path.exists(output_image):
|
|
||||||
if not args.colorize:
|
|
||||||
category2 = "high-tech"
|
|
||||||
dither_image(source_image,output_image, category2)
|
|
||||||
logging.info(" converted {}".format(fname))
|
|
||||||
logging.debug(output_image)
|
|
||||||
else:
|
|
||||||
logging.debug("Dithered version of {} found, skipping".format(fname))
|
|
||||||
|
|
||||||
prev_root = root
|
|
||||||
|
|
||||||
|
|
||||||
logging.info("Done dithering")
|
|
Before Width: | Height: | Size: 7.9 KiB |
BIN
dithers/fonk.png
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 24 KiB |
BIN
dithers/rd.png
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 23 KiB |
BIN
favicon.png
Before Width: | Height: | Size: 69 KiB |
BIN
fonts/exo2.ttf
BIN
fonts/exo2l.otf
@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from . import data
|
|
||||||
from . import math
|
|
||||||
from . import ordered
|
|
||||||
from . import diffusion
|
|
||||||
from . import palette
|
|
||||||
from . import utils
|
|
||||||
from .__version__ import __version__, version
|
|
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
__version__.py
|
|
||||||
-----------
|
|
||||||
|
|
||||||
:copyright: 2017-05-10 by hbldh <henrik.blidh@nedomkull.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
# Version information.
|
|
||||||
__version__ = "0.1.7"
|
|
||||||
version = __version__ # backwards compatibility name
|
|
@ -1,89 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
try:
|
|
||||||
import pathlib2 as pathlib
|
|
||||||
except ImportError:
|
|
||||||
import pathlib
|
|
||||||
|
|
||||||
try:
|
|
||||||
from urllib import urlopen
|
|
||||||
except ImportError:
|
|
||||||
from urllib.request import urlopen
|
|
||||||
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
|
|
||||||
def scene():
|
|
||||||
"""Chrono Cross PNG image used in Yliluoma's web page.
|
|
||||||
|
|
||||||
:return: The PIL image of the Chrono Cross scene.
|
|
||||||
|
|
||||||
"""
|
|
||||||
image_path = pathlib.Path(__file__).resolve().parent.joinpath("scene.png")
|
|
||||||
image_url = "http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/scene.png"
|
|
||||||
return _image(image_path, image_url)
|
|
||||||
|
|
||||||
|
|
||||||
def scene_undithered():
|
|
||||||
"""Chrono Cross PNG image rendered directly with specified palette.
|
|
||||||
|
|
||||||
:return: The PIL image of the undithered Chrono Cross scene.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _image(
|
|
||||||
pathlib.Path(__file__).resolve().parent.joinpath("scenenodither.png"),
|
|
||||||
"http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/scenenodither.png",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def scene_bayer0():
|
|
||||||
"""Chrono Cross PNG image dithered using ordered Bayer matrix method.
|
|
||||||
|
|
||||||
:return: The PIL image of the ordered Bayer matrix dithered
|
|
||||||
Chrono Cross scene.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return _image(
|
|
||||||
pathlib.Path(__file__).resolve().parent.joinpath("scenebayer0.png"),
|
|
||||||
"http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/scenebayer0.png",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _image(pth, url):
|
|
||||||
"""Load image specified in ``path``. If not present,
|
|
||||||
fetch it from ``url`` and store locally.
|
|
||||||
|
|
||||||
:param str or :class:`~pathlib.Path` pth:
|
|
||||||
:param str url: URL from where to fetch the image.
|
|
||||||
:return: The :class:`~PIL.Image` requested.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if pth.exists():
|
|
||||||
return Image.open(str(pth))
|
|
||||||
else:
|
|
||||||
r = urlopen(url)
|
|
||||||
with open(str(pth), "wb") as f:
|
|
||||||
f.write(r.read())
|
|
||||||
return _image(pth, url)
|
|
||||||
|
|
||||||
|
|
||||||
def palette():
|
|
||||||
return [
|
|
||||||
0x080000,
|
|
||||||
0x201A0B,
|
|
||||||
0x432817,
|
|
||||||
0x492910,
|
|
||||||
0x234309,
|
|
||||||
0x5D4F1E,
|
|
||||||
0x9C6B20,
|
|
||||||
0xA9220F,
|
|
||||||
0x2B347C,
|
|
||||||
0x2B7409,
|
|
||||||
0xD0CA40,
|
|
||||||
0xE8A077,
|
|
||||||
0x6A94AB,
|
|
||||||
0xD5C4B3,
|
|
||||||
0xFCE76E,
|
|
||||||
0xFCFAE2,
|
|
||||||
]
|
|
@ -1,193 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
:mod:`diffusion`
|
|
||||||
=======================
|
|
||||||
|
|
||||||
.. moduleauthor:: hbldh <henrik.blidh@swedwise.com>
|
|
||||||
Created on 2016-09-12, 11:34
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
_DIFFUSION_MAPS = {
|
|
||||||
"floyd-steinberg": (
|
|
||||||
(1, 0, 7 / 16),
|
|
||||||
(-1, 1, 3 / 16),
|
|
||||||
(0, 1, 5 / 16),
|
|
||||||
(1, 1, 1 / 16),
|
|
||||||
),
|
|
||||||
"atkinson": (
|
|
||||||
(1, 0, 1 / 8),
|
|
||||||
(2, 0, 1 / 8),
|
|
||||||
(-1, 1, 1 / 8),
|
|
||||||
(0, 1, 1 / 8),
|
|
||||||
(1, 1, 1 / 8),
|
|
||||||
(0, 2, 1 / 8),
|
|
||||||
),
|
|
||||||
"jarvis-judice-ninke": (
|
|
||||||
(1, 0, 7 / 48),
|
|
||||||
(2, 0, 5 / 48),
|
|
||||||
(-2, 1, 3 / 48),
|
|
||||||
(-1, 1, 5 / 48),
|
|
||||||
(0, 1, 7 / 48),
|
|
||||||
(1, 1, 5 / 48),
|
|
||||||
(2, 1, 3 / 48),
|
|
||||||
(-2, 2, 1 / 48),
|
|
||||||
(-1, 2, 3 / 48),
|
|
||||||
(0, 2, 5 / 48),
|
|
||||||
(1, 2, 3 / 48),
|
|
||||||
(2, 2, 1 / 48),
|
|
||||||
),
|
|
||||||
"stucki": (
|
|
||||||
(1, 0, 8 / 42),
|
|
||||||
(2, 0, 4 / 42),
|
|
||||||
(-2, 1, 2 / 42),
|
|
||||||
(-1, 1, 4 / 42),
|
|
||||||
(0, 1, 8 / 42),
|
|
||||||
(1, 1, 4 / 42),
|
|
||||||
(2, 1, 2 / 42),
|
|
||||||
(-2, 2, 1 / 42),
|
|
||||||
(-1, 2, 2 / 42),
|
|
||||||
(0, 2, 4 / 42),
|
|
||||||
(1, 2, 2 / 42),
|
|
||||||
(2, 2, 1 / 42),
|
|
||||||
),
|
|
||||||
"burkes": (
|
|
||||||
(1, 0, 8 / 32),
|
|
||||||
(2, 0, 4 / 32),
|
|
||||||
(-2, 1, 2 / 32),
|
|
||||||
(-1, 1, 4 / 32),
|
|
||||||
(0, 1, 8 / 32),
|
|
||||||
(1, 1, 4 / 32),
|
|
||||||
(2, 1, 2 / 32),
|
|
||||||
),
|
|
||||||
"sierra3": (
|
|
||||||
(1, 0, 5 / 32),
|
|
||||||
(2, 0, 3 / 32),
|
|
||||||
(-2, 1, 2 / 32),
|
|
||||||
(-1, 1, 4 / 32),
|
|
||||||
(0, 1, 5 / 32),
|
|
||||||
(1, 1, 4 / 32),
|
|
||||||
(2, 1, 2 / 32),
|
|
||||||
(-1, 2, 2 / 32),
|
|
||||||
(0, 2, 3 / 32),
|
|
||||||
(1, 2, 2 / 32),
|
|
||||||
),
|
|
||||||
"sierra2": (
|
|
||||||
(1, 0, 4 / 16),
|
|
||||||
(2, 0, 3 / 16),
|
|
||||||
(-2, 1, 1 / 16),
|
|
||||||
(-1, 1, 2 / 16),
|
|
||||||
(0, 1, 3 / 16),
|
|
||||||
(1, 1, 2 / 16),
|
|
||||||
(2, 1, 1 / 16),
|
|
||||||
),
|
|
||||||
"sierra-2-4a": (
|
|
||||||
(1, 0, 2 / 4),
|
|
||||||
(-1, 1, 1 / 4),
|
|
||||||
(0, 1, 1 / 4),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def error_diffusion_dithering(image, palette, method="floyd-steinberg", order=2):
|
|
||||||
"""Perform image dithering by error diffusion method.
|
|
||||||
|
|
||||||
.. note:: Error diffusion is totally unoptimized and therefore very slow.
|
|
||||||
It is included more as a reference implementation than as a useful
|
|
||||||
method.
|
|
||||||
|
|
||||||
Reference:
|
|
||||||
http://bisqwit.iki.fi/jutut/kuvat/ordered_dither/error_diffusion.txt
|
|
||||||
|
|
||||||
Quantization error of *current* pixel is added to the pixels
|
|
||||||
on the right and below according to the formulas below.
|
|
||||||
This works nicely for most static pictures, but causes
|
|
||||||
an avalanche of jittering artifacts if used in animation.
|
|
||||||
|
|
||||||
Floyd-Steinberg:
|
|
||||||
|
|
||||||
* 7
|
|
||||||
3 5 1 / 16
|
|
||||||
|
|
||||||
Jarvis-Judice-Ninke:
|
|
||||||
|
|
||||||
* 7 5
|
|
||||||
3 5 7 5 3
|
|
||||||
1 3 5 3 1 / 48
|
|
||||||
|
|
||||||
Stucki:
|
|
||||||
|
|
||||||
* 8 4
|
|
||||||
2 4 8 4 2
|
|
||||||
1 2 4 2 1 / 42
|
|
||||||
|
|
||||||
Burkes:
|
|
||||||
|
|
||||||
* 8 4
|
|
||||||
2 4 8 4 2 / 32
|
|
||||||
|
|
||||||
|
|
||||||
Sierra3:
|
|
||||||
|
|
||||||
* 5 3
|
|
||||||
2 4 5 4 2
|
|
||||||
2 3 2 / 32
|
|
||||||
|
|
||||||
Sierra2:
|
|
||||||
|
|
||||||
* 4 3
|
|
||||||
1 2 3 2 1 / 16
|
|
||||||
|
|
||||||
Sierra-2-4A:
|
|
||||||
|
|
||||||
* 2
|
|
||||||
1 1 / 4
|
|
||||||
|
|
||||||
Stevenson-Arce:
|
|
||||||
|
|
||||||
* . 32
|
|
||||||
12 . 26 . 30 . 16
|
|
||||||
. 12 . 26 . 12 .
|
|
||||||
5 . 12 . 12 . 5 / 200
|
|
||||||
|
|
||||||
Atkinson:
|
|
||||||
|
|
||||||
* 1 1 / 8
|
|
||||||
1 1 1
|
|
||||||
1
|
|
||||||
|
|
||||||
:param :class:`PIL.Image` image: The image to apply error
|
|
||||||
diffusion dithering to.
|
|
||||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
|
||||||
:param str method: The error diffusion map to use.
|
|
||||||
:param int order: Metric parameter ``ord`` to send to
|
|
||||||
:method:`numpy.linalg.norm`.
|
|
||||||
:return: The error diffusion dithered PIL image of type
|
|
||||||
"P" using the input palette.
|
|
||||||
|
|
||||||
"""
|
|
||||||
ni = np.array(image, "float")
|
|
||||||
|
|
||||||
diff_map = _DIFFUSION_MAPS.get(method.lower())
|
|
||||||
|
|
||||||
for y in range(ni.shape[0]):
|
|
||||||
for x in range(ni.shape[1]):
|
|
||||||
old_pixel = ni[y, x]
|
|
||||||
old_pixel[old_pixel < 0.0] = 0.0
|
|
||||||
old_pixel[old_pixel > 255.0] = 255.0
|
|
||||||
new_pixel = palette.pixel_closest_colour(old_pixel, order)
|
|
||||||
quantization_error = old_pixel - new_pixel
|
|
||||||
ni[y, x] = new_pixel
|
|
||||||
for dx, dy, diffusion_coefficient in diff_map:
|
|
||||||
xn, yn = x + dx, y + dy
|
|
||||||
if (0 <= xn < ni.shape[1]) and (0 <= yn < ni.shape[0]):
|
|
||||||
ni[yn, xn] += quantization_error * diffusion_coefficient
|
|
||||||
return palette.create_PIL_png_from_rgb_array(np.array(ni, "uint8"))
|
|
@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
exceptions
|
|
||||||
-----------
|
|
||||||
|
|
||||||
:copyright: 2017-05-10 by hbldh <henrik.blidh@nedomkull.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
|
|
||||||
class HitherDitherError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PaletteCouldNotBeCreatedError(Exception):
|
|
||||||
pass
|
|
@ -1,3 +0,0 @@
|
|||||||
from . import bayer
|
|
||||||
from . import yliluoma
|
|
||||||
from . import cluster
|
|
@ -1,88 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
bayer_dithering
|
|
||||||
-----------
|
|
||||||
|
|
||||||
:copyright: 2016-09-09 by hbldh <henrik.blidh@nedomkull.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
|
|
||||||
def B(n, transposed=False):
|
|
||||||
"""Get the Bayer matrix with side of length ``n``.
|
|
||||||
|
|
||||||
Will only work if ``n`` is a power of 2.
|
|
||||||
|
|
||||||
Reference: http://caca.zoy.org/study/part2.html
|
|
||||||
|
|
||||||
:param int n: Power of 2 side length of matrix.
|
|
||||||
:return: The Bayer matrix.
|
|
||||||
|
|
||||||
"""
|
|
||||||
return (1 + I(n, transposed)) / (1 + (n * n))
|
|
||||||
|
|
||||||
|
|
||||||
def I(n, transposed=False):
|
|
||||||
"""Get the index matrix with side of length ``n``.
|
|
||||||
|
|
||||||
Will only work if ``n`` is a power of 2.
|
|
||||||
|
|
||||||
Reference: http://caca.zoy.org/study/part2.html
|
|
||||||
|
|
||||||
:param int n: Power of 2 side length of matrix.
|
|
||||||
:param bool transposed:
|
|
||||||
:return: The index matrix.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if n == 2:
|
|
||||||
if transposed:
|
|
||||||
return np.array([[0, 3], [2, 1]], "int")
|
|
||||||
else:
|
|
||||||
return np.array([[0, 2], [3, 1]], "int")
|
|
||||||
else:
|
|
||||||
smaller_I = I(n >> 1, transposed)
|
|
||||||
if transposed:
|
|
||||||
return np.bmat(
|
|
||||||
[
|
|
||||||
[4 * smaller_I, 4 * smaller_I + 3],
|
|
||||||
[4 * smaller_I + 2, 4 * smaller_I + 1],
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return np.bmat(
|
|
||||||
[
|
|
||||||
[4 * smaller_I, 4 * smaller_I + 2],
|
|
||||||
[4 * smaller_I + 3, 4 * smaller_I + 1],
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def bayer_dithering(image, palette, thresholds, order=8):
|
|
||||||
"""Render the image using the ordered Bayer matrix dithering pattern.
|
|
||||||
|
|
||||||
:param :class:`PIL.Image` image: The image to apply
|
|
||||||
Bayer ordered dithering to.
|
|
||||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
|
||||||
:param thresholds: Thresholds to apply dithering at.
|
|
||||||
:param int order: The size of the Bayer matrix.
|
|
||||||
:return: The Bayer matrix dithered PIL image of type "P"
|
|
||||||
using the input palette.
|
|
||||||
|
|
||||||
"""
|
|
||||||
bayer_matrix = B(order)
|
|
||||||
ni = np.array(image, "uint8")
|
|
||||||
thresholds = np.array(thresholds, "uint8")
|
|
||||||
xx, yy = np.meshgrid(range(ni.shape[1]), range(ni.shape[0]))
|
|
||||||
xx %= order
|
|
||||||
yy %= order
|
|
||||||
factor_threshold_matrix = np.expand_dims(bayer_matrix[yy, xx], axis=2) * thresholds
|
|
||||||
new_image = ni + factor_threshold_matrix
|
|
||||||
return palette.create_PIL_png_from_rgb_array(new_image)
|
|
@ -1,67 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
bayer_dithering
|
|
||||||
-----------
|
|
||||||
|
|
||||||
:copyright: 2016-09-09 by hbldh <henrik.blidh@nedomkull.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
_CLUSTER_DOT_MATRICES = {
|
|
||||||
4: np.array([[12, 5, 6, 13], [4, 0, 1, 7], [11, 3, 2, 8], [15, 10, 9, 14]], "float")
|
|
||||||
/ 16.0,
|
|
||||||
8: np.array(
|
|
||||||
[
|
|
||||||
[24, 10, 12, 26, 35, 47, 49, 37],
|
|
||||||
[8, 0, 2, 14, 45, 59, 61, 51],
|
|
||||||
[22, 6, 4, 16, 43, 57, 63, 53],
|
|
||||||
[30, 20, 18, 28, 33, 41, 55, 39],
|
|
||||||
[34, 46, 48, 36, 25, 11, 13, 27],
|
|
||||||
[44, 57, 60, 50, 9, 1, 3, 15],
|
|
||||||
[42, 56, 62, 52, 23, 7, 5, 17],
|
|
||||||
[32, 40, 54, 38, 31, 21, 19, 29],
|
|
||||||
],
|
|
||||||
"float",
|
|
||||||
)
|
|
||||||
/ 64.0,
|
|
||||||
(5, 3): np.array([[9, 3, 0, 6, 12], [10, 4, 1, 7, 13], [11, 5, 2, 8, 14]], "float")
|
|
||||||
/ 15.0,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def cluster_dot_dithering(image, palette, thresholds, order=4):
|
|
||||||
"""Render the image using the ordered Bayer matrix dithering pattern.
|
|
||||||
|
|
||||||
Reference: http://caca.zoy.org/study/part2.html
|
|
||||||
|
|
||||||
:param :class:`PIL.Image` image: The image to apply the
|
|
||||||
ordered dithering to.
|
|
||||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
|
||||||
:param thresholds: Thresholds to apply dithering at.
|
|
||||||
:param int order: The size of the Bayer matrix.
|
|
||||||
:return: The Bayer matrix dithered PIL image of type "P"
|
|
||||||
using the input palette.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
cluster_dot_matrix = _CLUSTER_DOT_MATRICES.get(order)
|
|
||||||
if cluster_dot_matrix is None:
|
|
||||||
raise NotImplementedError("Only order 4 and 8 is implemented as of yet.")
|
|
||||||
ni = np.array(image, "uint8")
|
|
||||||
thresholds = np.array(thresholds, "uint8")
|
|
||||||
xx, yy = np.meshgrid(range(ni.shape[1]), range(ni.shape[0]))
|
|
||||||
xx %= order
|
|
||||||
yy %= order
|
|
||||||
factor_threshold_matrix = (
|
|
||||||
np.expand_dims(cluster_dot_matrix[yy, xx], axis=2) * thresholds
|
|
||||||
)
|
|
||||||
new_image = ni + factor_threshold_matrix
|
|
||||||
return palette.create_PIL_png_from_rgb_array(new_image)
|
|
@ -1 +0,0 @@
|
|||||||
from ._algorithm_one import yliluomas_1_ordered_dithering
|
|
@ -1,180 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
algorithm_one
|
|
||||||
-----------
|
|
||||||
|
|
||||||
:copyright: 2016-09-12 by hbldh <henrik.blidh@nedomkull.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from ._utils import color_compare, CCIR_LUMINOSITY
|
|
||||||
from ..bayer import I
|
|
||||||
|
|
||||||
|
|
||||||
def _get_mixing_plan_matrix(palette, order=8):
|
|
||||||
mixing_matrix = []
|
|
||||||
colours = {}
|
|
||||||
colour_component_distances = []
|
|
||||||
|
|
||||||
nn = order * order
|
|
||||||
for i in range(len(palette)):
|
|
||||||
for j in range(i, len(palette)):
|
|
||||||
for ratio in range(0, nn):
|
|
||||||
if i == j and ratio != 0:
|
|
||||||
break
|
|
||||||
# Determine the two component colors.
|
|
||||||
c_mix = _colour_combine(palette, i, j, ratio / nn)
|
|
||||||
hex_colour = palette.rgb2hex(*c_mix.tolist())
|
|
||||||
colours[hex_colour] = (i, j, ratio / nn)
|
|
||||||
mixing_matrix.append(c_mix)
|
|
||||||
|
|
||||||
c1 = np.array(palette[i], "int")
|
|
||||||
c2 = np.array(palette[j], "int")
|
|
||||||
cmpval = (
|
|
||||||
color_compare(c1, c2)
|
|
||||||
* 0.1
|
|
||||||
* (np.abs((ratio / float(nn)) - 0.5) + 0.5)
|
|
||||||
)
|
|
||||||
colour_component_distances.append(cmpval)
|
|
||||||
|
|
||||||
mixing_matrix = np.array(mixing_matrix)
|
|
||||||
colour_component_distances = np.array(colour_component_distances)
|
|
||||||
|
|
||||||
for c in mixing_matrix:
|
|
||||||
assert palette.rgb2hex(*c.tolist()) in colours
|
|
||||||
|
|
||||||
return mixing_matrix, colours, colour_component_distances
|
|
||||||
|
|
||||||
|
|
||||||
def _colour_combine(palette, i, j, ratio):
|
|
||||||
c1, c2 = np.array(palette[i], "int"), np.array(palette[j], "int")
|
|
||||||
return np.array(c1 + ratio * (c2 - c1), "uint8")
|
|
||||||
|
|
||||||
|
|
||||||
def _improved_mixing_error_fcn(
|
|
||||||
colour, mixing_matrix, colour_component_distances, luma_mat=None
|
|
||||||
):
|
|
||||||
"""Compares two colours using the Psychovisual model.
|
|
||||||
|
|
||||||
The simplest way to adjust the psychovisual model is to
|
|
||||||
add some code that considers the difference between the
|
|
||||||
two pixel values that are being mixed in the dithering
|
|
||||||
process, and penalizes combinations that differ too much.
|
|
||||||
|
|
||||||
Wikipedia has an entire article about the topic of comparing
|
|
||||||
two color values. Most of the improved color comparison
|
|
||||||
functions are based on the CIE colorspace, but simple
|
|
||||||
improvements can be done in the RGB space too. Such a simple
|
|
||||||
improvement is shown below. We might call this RGBL, for
|
|
||||||
luminance-weighted RGB.
|
|
||||||
|
|
||||||
:param :class:`numpy.ndarray` colour: The colour to estimate error to.
|
|
||||||
:param :class:`numpy.ndarray` mixing_matrix: The rgb
|
|
||||||
values of mixed colours.
|
|
||||||
:param :class:`numpy.ndarray` colour_component_distances: The colour
|
|
||||||
distance of the mixed colours.
|
|
||||||
:return: :class:`numpy.ndarray`
|
|
||||||
|
|
||||||
"""
|
|
||||||
colour = np.array(colour, "int")
|
|
||||||
if luma_mat is None:
|
|
||||||
luma_mat = mixing_matrix.dot(CCIR_LUMINOSITY / 1000.0 / 255.0)
|
|
||||||
luma_colour = colour.dot(CCIR_LUMINOSITY) / (255.0 * 1000.0)
|
|
||||||
luma_diff_squared = (luma_mat - luma_colour) ** 2
|
|
||||||
diff_colour_squared = ((colour - mixing_matrix) / 255.0) ** 2
|
|
||||||
cmpvals = diff_colour_squared.dot(CCIR_LUMINOSITY) / 1000.0
|
|
||||||
cmpvals *= 0.75
|
|
||||||
cmpvals += luma_diff_squared
|
|
||||||
cmpvals += colour_component_distances
|
|
||||||
return cmpvals
|
|
||||||
|
|
||||||
|
|
||||||
def yliluomas_1_ordered_dithering(image, palette, order=8):
|
|
||||||
"""A dithering method that weighs in color combinations of palette.
|
|
||||||
|
|
||||||
N.B. tri-tone dithering is not implemented.
|
|
||||||
|
|
||||||
:param :class:`PIL.Image` image: The image to apply
|
|
||||||
Bayer ordered dithering to.
|
|
||||||
:param :class:`~hitherdither.colour.Palette` palette: The palette to use.
|
|
||||||
:param int order: The Bayer matrix size to use.
|
|
||||||
:return: The dithered PIL image of type "P" using the input palette.
|
|
||||||
|
|
||||||
"""
|
|
||||||
bayer_matrix = I(order, transposed=True) / 64.0
|
|
||||||
ni = np.array(image, "uint8")
|
|
||||||
xx, yy = np.meshgrid(range(ni.shape[1]), range(ni.shape[0]))
|
|
||||||
factor_matrix = bayer_matrix[yy % order, xx % order]
|
|
||||||
|
|
||||||
# Prepare all precalculated mixed colours and their respective
|
|
||||||
mixing_matrix, colour_map, colour_component_distances = _get_mixing_plan_matrix(
|
|
||||||
palette
|
|
||||||
)
|
|
||||||
mixing_matrix = np.array(mixing_matrix, "int")
|
|
||||||
luma_mat = mixing_matrix.dot(CCIR_LUMINOSITY / 1000.0 / 255.0)
|
|
||||||
|
|
||||||
color_matrix = np.zeros(ni.shape[:2], dtype="uint8")
|
|
||||||
for x, y in zip(np.nditer(xx), np.nditer(yy)):
|
|
||||||
|
|
||||||
min_index = np.argmin(
|
|
||||||
_improved_mixing_error_fcn(
|
|
||||||
ni[y, x, :], mixing_matrix, colour_component_distances, luma_mat
|
|
||||||
)
|
|
||||||
)
|
|
||||||
closest_mix_colour = mixing_matrix[min_index, :].tolist()
|
|
||||||
closest_mix_hexcolour = palette.rgb2hex(*closest_mix_colour)
|
|
||||||
plan = colour_map.get(closest_mix_hexcolour)
|
|
||||||
color_matrix[y, x] = plan[1] if (factor_matrix[y, x] < plan[-1]) else plan[0]
|
|
||||||
|
|
||||||
return palette.create_PIL_png_from_closest_colour(color_matrix)
|
|
||||||
|
|
||||||
|
|
||||||
def _evaluate_mixing_error(
|
|
||||||
desired_colour,
|
|
||||||
mixed_colour,
|
|
||||||
component_colour_1,
|
|
||||||
component_colour_2,
|
|
||||||
ratio,
|
|
||||||
component_colour_compare_value=None,
|
|
||||||
):
|
|
||||||
"""Compare colours and weigh in component difference.
|
|
||||||
|
|
||||||
double EvaluateMixingError(int r,int g,int b,
|
|
||||||
int r0,int g0,int b0,
|
|
||||||
int r1,int g1,int b1,
|
|
||||||
int r2,int g2,int b2,
|
|
||||||
double ratio)
|
|
||||||
{
|
|
||||||
return ColorCompare(r,g,b, r0,g0,b0)
|
|
||||||
+ ColorCompare(r1,g1,b1, r2,g2,b2) * 0.1
|
|
||||||
* (fabs(ratio-0.5)+0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
:param desired_colour:
|
|
||||||
:param mixed_colour:
|
|
||||||
:param component_colour_1:
|
|
||||||
:param component_colour_2:
|
|
||||||
:param ratio:
|
|
||||||
:param component_colour_compare_value:
|
|
||||||
:return:
|
|
||||||
|
|
||||||
"""
|
|
||||||
if component_colour_compare_value is None:
|
|
||||||
return color_compare(desired_colour, mixed_colour) + (
|
|
||||||
color_compare(component_colour_1, component_colour_2)
|
|
||||||
* 0.1
|
|
||||||
* (np.abs(ratio - 0.5) + 0.5)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
color_compare(desired_colour, mixed_colour) + component_colour_compare_value
|
|
||||||
)
|
|
@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
_utils
|
|
||||||
-----------
|
|
||||||
|
|
||||||
:copyright: 2016-09-23 by hbldh <henrik.blidh@nedomkull.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
# CCIR 601 luminosity
|
|
||||||
CCIR_LUMINOSITY = np.array([299.0, 587.0, 114.0])
|
|
||||||
|
|
||||||
|
|
||||||
def color_compare(c1, c2):
|
|
||||||
"""Compare the difference of two RGB values, weigh by CCIR 601 luminosity
|
|
||||||
|
|
||||||
double ColorCompare(int r1,int g1,int b1, int r2,int g2,int b2)
|
|
||||||
{
|
|
||||||
double luma1 = (r1*299 + g1*587 + b1*114) / (255.0*1000);
|
|
||||||
double luma2 = (r2*299 + g2*587 + b2*114) / (255.0*1000);
|
|
||||||
double lumadiff = luma1-luma2;
|
|
||||||
double diffR = (r1-r2)/255.0, diffG = (g1-g2)/255.0, diffB = (b1-b2)/255.0;
|
|
||||||
return (diffR*diffR*0.299 + diffG*diffG*0.587 + diffB*diffB*0.114)*0.75
|
|
||||||
+ lumadiff*lumadiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
:return: float
|
|
||||||
|
|
||||||
"""
|
|
||||||
luma_diff = c1.dot(CCIR_LUMINOSITY) / (255.0 * 1000.0) - c2.dot(CCIR_LUMINOSITY) / (
|
|
||||||
255.0 * 1000.0
|
|
||||||
)
|
|
||||||
diff_col = (c1 - c2) / 255.0
|
|
||||||
return ((diff_col ** 2).dot(CCIR_LUMINOSITY / 1000.0) * 0.75) + (luma_diff ** 2)
|
|
@ -1,246 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
palette
|
|
||||||
-----------
|
|
||||||
|
|
||||||
:copyright: 2016-09-09 by hbldh <henrik.blidh@nedomkull.com>
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from PIL import Image
|
|
||||||
from PIL.ImagePalette import ImagePalette
|
|
||||||
|
|
||||||
from hitherdither.exceptions import PaletteCouldNotBeCreatedError
|
|
||||||
|
|
||||||
try:
|
|
||||||
string_type = basestring
|
|
||||||
except NameError:
|
|
||||||
string_type = str
|
|
||||||
|
|
||||||
|
|
||||||
def hex2rgb(h):
|
|
||||||
if isinstance(h, string_type):
|
|
||||||
return hex2rgb(int(h[1:] if h.startswith("#") else h, 16))
|
|
||||||
return (h >> 16) & 0xFF, (h >> 8) & 0xFF, h & 0xFF
|
|
||||||
|
|
||||||
|
|
||||||
def rgb2hex(r, g, b):
|
|
||||||
return (r << 16) + (g << 8) + b
|
|
||||||
|
|
||||||
|
|
||||||
def _get_all_present_colours(im):
|
|
||||||
"""Returns a dict of RGB colours present.
|
|
||||||
|
|
||||||
N.B. Do not use this except for testing purposes.
|
|
||||||
|
|
||||||
Reference: http://stackoverflow.com/a/4643911
|
|
||||||
|
|
||||||
:param im: The image to get number of colours in.
|
|
||||||
:type im: :class:`~PIL.Image.Image`
|
|
||||||
:return: A dict of contained RGB colours as keys.
|
|
||||||
:rtype: dict
|
|
||||||
|
|
||||||
"""
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
by_color = defaultdict(int)
|
|
||||||
for pixel in im.getdata():
|
|
||||||
by_color[pixel] += 1
|
|
||||||
return by_color
|
|
||||||
|
|
||||||
|
|
||||||
class Palette(object):
|
|
||||||
"""The :mod:`~hitherdither` implementation of a colour palette.
|
|
||||||
|
|
||||||
Can be instantiated in from colour specifications in the following forms:
|
|
||||||
|
|
||||||
- ``uint8`` numpy array of size ``[N x 3]``
|
|
||||||
- ``uint8`` numpy array of size ``[3N]``
|
|
||||||
- :class:`~PIL.ImagePalette.ImagePalette`
|
|
||||||
- :class:`~PIL.Image.Image`
|
|
||||||
- list of hex values
|
|
||||||
- list of RGB tuples
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, data):
|
|
||||||
if isinstance(data, np.ndarray):
|
|
||||||
if data.ndim == 1:
|
|
||||||
self.colours = data.reshape((3, len(data) // 3))
|
|
||||||
else:
|
|
||||||
self.colours = data
|
|
||||||
self.hex = [rgb2hex(*colour) for colour in data]
|
|
||||||
elif isinstance(data, ImagePalette):
|
|
||||||
_tmp = np.frombuffer(data.palette, "uint8")
|
|
||||||
self.colours = _tmp.reshape((3, len(_tmp) // 3))
|
|
||||||
self.hex = [rgb2hex(*colour) for colour in data]
|
|
||||||
elif isinstance(data, Image.Image):
|
|
||||||
if data.palette is None:
|
|
||||||
raise PaletteCouldNotBeCreatedError(
|
|
||||||
"Image of mode {0} has no PIL palette. "
|
|
||||||
"Make sure it is of mode P.".format(data.mode)
|
|
||||||
)
|
|
||||||
_colours = data.getcolors()
|
|
||||||
_n_colours = len(_colours)
|
|
||||||
_tmp = np.array(data.getpalette())[: 3 * _n_colours]
|
|
||||||
self.colours = _tmp.reshape((3, len(_tmp) // 3)).T
|
|
||||||
self.hex = [rgb2hex(*colour) for colour in self]
|
|
||||||
elif isinstance(data, (list, tuple)):
|
|
||||||
if isinstance(data[0], string_type):
|
|
||||||
# Assume hex strings
|
|
||||||
self.hex = data
|
|
||||||
self.colours = np.array([hex2rgb(c) for c in data])
|
|
||||||
elif isinstance(data[0], int):
|
|
||||||
# Assume hex values
|
|
||||||
self.hex = data # TODO: Convert to hex string.
|
|
||||||
self.colours = np.array([hex2rgb(c) for c in data])
|
|
||||||
else:
|
|
||||||
# Assume RGB tuples
|
|
||||||
self.colours = np.array(data)
|
|
||||||
self.hex = [rgb2hex(*colour) for colour in data]
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
for colour in self.colours:
|
|
||||||
yield colour
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return self.colours.shape[0]
|
|
||||||
|
|
||||||
def __getitem__(self, item):
|
|
||||||
if isinstance(item, int):
|
|
||||||
return self.colours[item, :]
|
|
||||||
else:
|
|
||||||
raise IndexError("Can only reference colours by integer values.")
|
|
||||||
|
|
||||||
def render(self, colours):
|
|
||||||
return np.array(np.take(self.colours, colours, axis=0), "uint8")
|
|
||||||
|
|
||||||
def image_distance(self, image, order=2):
|
|
||||||
ni = np.array(image, "float")
|
|
||||||
distances = np.zeros((ni.shape[0], ni.shape[1], len(self)), "float")
|
|
||||||
for i, colour in enumerate(self):
|
|
||||||
distances[:, :, i] = np.linalg.norm(ni - colour, ord=order, axis=2)
|
|
||||||
return distances
|
|
||||||
|
|
||||||
def image_closest_colour(self, image, order=2):
|
|
||||||
return np.argmin(self.image_distance(image, order=order), axis=2)
|
|
||||||
|
|
||||||
def pixel_distance(self, pixel, order=2):
|
|
||||||
return np.array([np.linalg.norm(pixel - colour, ord=order) for colour in self])
|
|
||||||
|
|
||||||
def pixel_closest_colour(self, pixel, order=2):
|
|
||||||
return self.colours[
|
|
||||||
np.argmin(self.pixel_distance(pixel, order=order)), :
|
|
||||||
].copy()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_by_kmeans(cls, image):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def create_by_median_cut(cls, image, n=16, dim=None):
|
|
||||||
img = np.array(image)
|
|
||||||
# Create pixel buckets to simplify sorting and splitting.
|
|
||||||
if img.ndim == 3:
|
|
||||||
pixels = img.reshape((img.shape[0] * img.shape[1], img.shape[2]))
|
|
||||||
elif img.ndim == 2:
|
|
||||||
pixels = img.reshape((img.shape[0] * img.shape[1], 1))
|
|
||||||
|
|
||||||
def median_cut(p, dim=None):
|
|
||||||
"""Median cut method.
|
|
||||||
|
|
||||||
Reference:
|
|
||||||
https://en.wikipedia.org/wiki/Median_cut
|
|
||||||
|
|
||||||
:param p: The pixel array to split in two.
|
|
||||||
:return: Two numpy arrays, split by median cut method.
|
|
||||||
"""
|
|
||||||
if dim is not None:
|
|
||||||
sort_dim = dim
|
|
||||||
else:
|
|
||||||
mins = p.min(axis=0)
|
|
||||||
maxs = p.max(axis=0)
|
|
||||||
sort_dim = np.argmax(maxs - mins)
|
|
||||||
|
|
||||||
argument = np.argsort(p[:, sort_dim])
|
|
||||||
p = p[argument, :]
|
|
||||||
m = np.median(p[:, sort_dim])
|
|
||||||
split_mask = p[:, sort_dim] >= m
|
|
||||||
return [p[~split_mask, :].copy(), p[split_mask, :].copy()]
|
|
||||||
|
|
||||||
# Do actual splitting loop.
|
|
||||||
bins = [
|
|
||||||
pixels,
|
|
||||||
]
|
|
||||||
while len(bins) < n:
|
|
||||||
new_bins = []
|
|
||||||
for bin in bins:
|
|
||||||
new_bins += median_cut(bin, dim)
|
|
||||||
bins = new_bins
|
|
||||||
|
|
||||||
# Average over pixels in each bin to create
|
|
||||||
colours = np.array(
|
|
||||||
[np.array(bin.mean(axis=0).round(), "uint8") for bin in bins], "uint8"
|
|
||||||
)
|
|
||||||
return cls(colours)
|
|
||||||
|
|
||||||
def create_PIL_png_from_closest_colour(self, cc):
|
|
||||||
"""Create a ``P`` PIL image with this palette.
|
|
||||||
|
|
||||||
Avoids the PIL dithering in favour of our own.
|
|
||||||
|
|
||||||
Reference: http://stackoverflow.com/a/29438149
|
|
||||||
|
|
||||||
:param :class:`numpy.ndarray` cc: A ``[M x N]`` array with integer
|
|
||||||
values representing palette colour indices to build image from.
|
|
||||||
:return: A :class:`PIL.Image.Image` image of mode ``P``.
|
|
||||||
|
|
||||||
"""
|
|
||||||
pa_image = Image.new("P", cc.shape[::-1])
|
|
||||||
pa_image.putpalette(self.colours.flatten().tolist())
|
|
||||||
im = Image.fromarray(np.array(cc, "uint8")).im.convert("P", 0, pa_image.im)
|
|
||||||
try:
|
|
||||||
# Pillow >= 4
|
|
||||||
return pa_image._new(im)
|
|
||||||
except AttributeError:
|
|
||||||
# Pillow < 4
|
|
||||||
return pa_image._makeself(im)
|
|
||||||
|
|
||||||
def create_PIL_png_from_rgb_array(self, img_array):
|
|
||||||
"""Create a ``P`` PIL image from a RGB image with this palette.
|
|
||||||
|
|
||||||
Avoids the PIL dithering in favour of our own.
|
|
||||||
|
|
||||||
Reference: http://stackoverflow.com/a/29438149
|
|
||||||
|
|
||||||
:param :class:`numpy.ndarray` img_array: A ``[M x N x 3]`` uint8
|
|
||||||
array representing RGB colours.
|
|
||||||
:return: A :class:`PIL.Image.Image` image of mode ``P`` with colours
|
|
||||||
available in this palette.
|
|
||||||
|
|
||||||
"""
|
|
||||||
cc = self.image_closest_colour(img_array, order=2)
|
|
||||||
pa_image = Image.new("P", cc.shape[::-1])
|
|
||||||
pa_image.putpalette(self.colours.flatten().tolist())
|
|
||||||
im = Image.fromarray(np.array(cc, "uint8")).im.convert("P", 0, pa_image.im)
|
|
||||||
try:
|
|
||||||
# Pillow >= 4
|
|
||||||
return pa_image._new(im)
|
|
||||||
except AttributeError:
|
|
||||||
# Pillow < 4
|
|
||||||
return pa_image._makeself(im)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def hex2rgb(x):
|
|
||||||
return hex2rgb(x)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def rgb2hex(r, g, b):
|
|
||||||
return rgb2hex(r, g, b)
|
|
@ -1,27 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
:mod:`utils`
|
|
||||||
=======================
|
|
||||||
|
|
||||||
.. moduleauthor:: hbldh <henrik.blidh@swedwise.com>
|
|
||||||
Created on 2016-09-12, 09:50
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import division
|
|
||||||
from __future__ import print_function
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
|
|
||||||
def np2pil(img):
|
|
||||||
return Image.fromarray(np.array(img, "uint8"))
|
|
||||||
|
|
||||||
|
|
||||||
def pil2np(img):
|
|
||||||
return np.array(img, "uint8")
|
|
Before Width: | Height: | Size: 4.5 KiB |
BIN
images/fonk.png
Before Width: | Height: | Size: 61 KiB |
BIN
images/git.png
Before Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 209 KiB |
BIN
images/piped.png
Before Width: | Height: | Size: 267 KiB |
BIN
images/rd.png
Before Width: | Height: | Size: 91 KiB |
BIN
images/rss.png
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 551 KiB |
Before Width: | Height: | Size: 749 KiB |
Before Width: | Height: | Size: 182 KiB |
Before Width: | Height: | Size: 570 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 400 KiB |
Before Width: | Height: | Size: 209 KiB |
196
index.html
@ -1,196 +0,0 @@
|
|||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head profile="https://thedroth.rocks">
|
|
||||||
<link rel="icon"
|
|
||||||
type="image/png"
|
|
||||||
href="favicon.png" />
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<title>TheDroth Rocks! </title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<link rel="stylesheet" href="css/picnic.min.css">
|
|
||||||
<style>
|
|
||||||
aside a.top {
|
|
||||||
font-size: 0;
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0;
|
|
||||||
font-weight: bold;
|
|
||||||
width: 180px;
|
|
||||||
padding: .6em 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
border-radius: .3em .3em 0 0;
|
|
||||||
transition: all .3s ease;
|
|
||||||
}
|
|
||||||
aside a.top.visible {
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
aside .links a.button {
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
@media all and (max-width: 1000px) {
|
|
||||||
aside a.pseudo.top {
|
|
||||||
background: rgba(255, 255, 255, .8);
|
|
||||||
width: 100%;
|
|
||||||
left: 0;
|
|
||||||
text-align: center;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.documentation article > h2 {
|
|
||||||
margin: -2em 0 .6em;
|
|
||||||
padding: 3em 0 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
.documentation article > h3 {
|
|
||||||
margin-bottom: .6em;
|
|
||||||
}
|
|
||||||
.documentation aside h2 {
|
|
||||||
margin-top: 0;
|
|
||||||
padding: 1.25em 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
.documentation aside a.pseudo {
|
|
||||||
color: #0074D9;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.documentation > section {
|
|
||||||
background: #fff;
|
|
||||||
text-align: left;
|
|
||||||
width: 90%;
|
|
||||||
max-width: 960px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 80px 0 0;
|
|
||||||
}
|
|
||||||
.documentation article > h1 {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0.6em 0;
|
|
||||||
font-size: 2em;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
.documentation aside a.button {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.documentation pre[class*="language-"] {
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
.documentation .index pre {
|
|
||||||
margin: 0;
|
|
||||||
font-size: .9em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav class="fedi">
|
|
||||||
<!-- <font size="1.5em"> -->
|
|
||||||
<a href="https://thedroth.rocks" class="brand">
|
|
||||||
<img class="logo" src="dithers/thedroth_logo.png" />
|
|
||||||
|
|
||||||
</a>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- responsive-->
|
|
||||||
<input id="bmenub" type="checkbox" class="show">
|
|
||||||
<label for="bmenub" class="burger pseudo button">меню</label>
|
|
||||||
|
|
||||||
<div class="menu">
|
|
||||||
<a href="index.html#proxy" class="pseudo button">Проксирующие сервисы</a>
|
|
||||||
<a href="index.html#selfhosted" class="pseudo button">Собственные сервисы</a>
|
|
||||||
<a href="index.html#blog" class="pseudo button">TheДротский бложик</a>
|
|
||||||
</div>
|
|
||||||
<!-- </font> -->
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main id="home" class="documentation">
|
|
||||||
<section class="flex">
|
|
||||||
<article class="card four-fifth-1000">
|
|
||||||
<header>TheDrothские сервисы</header>
|
|
||||||
<section class="flex">
|
|
||||||
<article class="card">
|
|
||||||
<center>
|
|
||||||
<header id="proxy">Проксирующие</header>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>Piped</h4>
|
|
||||||
<p>Лёгкий приватный интерфейс для YouTube, умеюший автоматически проматывать рекламные вставки. Зачем кормить гугл, если можно не кормить...</p>
|
|
||||||
<a href="https://piped.thedroth.rocks"><img src="dithers/piped.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>Teddit</h4>
|
|
||||||
<p>Быстрый и лёгкий фронтэнд для Reddit</p>
|
|
||||||
<a href="https://teddit.thedroth.rocks"><img src="dithers/teddit.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
<!-- <hr>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>Nitter</h4>
|
|
||||||
<p>Интерфейс для Twitter, избавленный от всего лишнего</p>
|
|
||||||
<a href="https://nitter.thedroth.rocks"><img src="dithers/nitter.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
<hr>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>Rural Dictionary</h4>
|
|
||||||
<p>Фронтэнд для Urban Dictionary. Знакомься с современным фольклором и сленгом без корпоративной слежки!</p>
|
|
||||||
<a href="https://rd.thedroth.rocks"><img src="dithers/rd.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>WikiLess</h4>
|
|
||||||
<p>Wikipedia без ненужных элементов и трекеров</p>
|
|
||||||
<a href="https://wiki.thedroth.rocks"><img src="dithers/wikiless.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
</center>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
<section class="flex">
|
|
||||||
<article class="card">
|
|
||||||
<center>
|
|
||||||
<header id="selfhosted">Собственные</header>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>Git</h4>
|
|
||||||
<p>Хранилище исходных кодов</p>
|
|
||||||
<a href="https://git.thedroth.rocks"><img src="dithers/git.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>TheДротский Фонк</h4>
|
|
||||||
<p>FunkWhale — хостинг музыки и подкастов</p>
|
|
||||||
<a href="https://fonk.thedroth.rocks"><img src="dithers/fonk.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<div class="flex">
|
|
||||||
<h4>RSS Bridge</h4>
|
|
||||||
<p>Конвертация новостных лент в RSS</p>
|
|
||||||
<a href="https://rss.thedroth.rocks"><img src="dithers/rss.png" width="60%"></a>
|
|
||||||
</div>
|
|
||||||
</center>
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
</article>
|
|
||||||
<article class="card four-fifth-1000">
|
|
||||||
<section class="flex">
|
|
||||||
<article class="card">
|
|
||||||
<div class="flex" id="blog"> <center>
|
|
||||||
<span>
|
|
||||||
<iframe style="border:none;" width="90%" height="700px" seamless src="https://blog.thedroth.rocks"></iframe> </span>
|
|
||||||
<span>
|
|
||||||
|
|
||||||
</center>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</article>
|
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
<section>
|
|
||||||
<hr>
|
|
||||||
<a href="https://gitflic.ru/project/the_sn4il/thedroth-rocks">Исходный код сайта</a>
|
|
||||||
</section>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
4
js/jquery.min.js
vendored
@ -1,8 +0,0 @@
|
|||||||
function dropme() {
|
|
||||||
var x = document.getElementById("rtopnav");
|
|
||||||
if (x.className === "topnav") {
|
|
||||||
x.className += " responsive";
|
|
||||||
} else {
|
|
||||||
x.className = "topnav";
|
|
||||||
}
|
|
||||||
}
|
|
36
js/popup.js
@ -1,36 +0,0 @@
|
|||||||
function openModal() {
|
|
||||||
document.getElementById("myModal").style.display = "block";
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeModal() {
|
|
||||||
document.getElementById("myModal").style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
var slideIndex = 1;
|
|
||||||
showSlides(slideIndex);
|
|
||||||
|
|
||||||
function plusSlides(n) {
|
|
||||||
showSlides(slideIndex += n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentSlide(n) {
|
|
||||||
showSlides(slideIndex = n);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showSlides(n) {
|
|
||||||
var i;
|
|
||||||
var slides = document.getElementsByClassName("mySlides");
|
|
||||||
var dots = document.getElementsByClassName("demo");
|
|
||||||
var captionText = document.getElementById("caption");
|
|
||||||
if (n > slides.length) {slideIndex = 1}
|
|
||||||
if (n < 1) {slideIndex = slides.length}
|
|
||||||
for (i = 0; i < slides.length; i++) {
|
|
||||||
slides[i].style.display = "none";
|
|
||||||
}
|
|
||||||
for (i = 0; i < dots.length; i++) {
|
|
||||||
dots[i].className = dots[i].className.replace(" active", "");
|
|
||||||
}
|
|
||||||
slides[slideIndex-1].style.display = "block";
|
|
||||||
dots[slideIndex-1].className += " active";
|
|
||||||
captionText.innerHTML = dots[slideIndex-1].alt;
|
|
||||||
}
|
|
4
public/404.html
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<!doctype html><html lang=en dir=auto><head><meta charset=utf-8><meta http-equiv=x-ua-compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=robots content="index, follow"><title>404 Page not found | TheDrothRocks!</title><meta name=keywords content><meta name=description content="ExampleSite description"><meta name=author content="Me"><link rel=canonical href=https://thedroth.rocks/404.html><meta name=google-site-verification content="XYZabc"><meta name=yandex-verification content="XYZabc"><meta name=msvalidate.01 content="XYZabc"><link crossorigin=anonymous href=/assets/css/stylesheet.5cfc680b1eeaeef9efbced92d46c2a9e876b72ee14fba85846afc4cff9e6e6f8.css integrity="sha256-XPxoCx7q7vnvvO2S1Gwqnodrcu4U+6hYRq/Ez/nm5vg=" rel="preload stylesheet" as=style><link rel=icon href=https://thedroth.rocks/images/favicon.png><link rel=icon type=image/png sizes=16x16 href=https://thedroth.rocks/images/favicon.png><link rel=icon type=image/png sizes=32x32 href=https://thedroth.rocks/images/favicon.png><link rel=apple-touch-icon href=https://thedroth.rocks/%3Clink%20/%20abs%20url%3E><link rel=mask-icon href=https://thedroth.rocks/%3Clink%20/%20abs%20url%3E><meta name=theme-color content="#2e2e33"><meta name=msapplication-TileColor content="#2e2e33"><noscript><style>#theme-toggle,.top-link{display:none}</style><style>@media(prefers-color-scheme:dark){:root{--theme:rgb(29, 30, 32);--entry:rgb(46, 46, 51);--primary:rgb(218, 218, 219);--secondary:rgb(155, 156, 157);--tertiary:rgb(65, 66, 68);--content:rgb(196, 196, 197);--hljs-bg:rgb(46, 46, 51);--code-bg:rgb(55, 56, 62);--border:rgb(51, 51, 51)}.list{background:var(--theme)}.list:not(.dark)::-webkit-scrollbar-track{background:0 0}.list:not(.dark)::-webkit-scrollbar-thumb{border-color:var(--theme)}}</style></noscript><meta property="og:title" content="404 Page not found"><meta property="og:description" content="ExampleSite description"><meta property="og:type" content="website"><meta property="og:url" content="https://thedroth.rocks/404.html"><meta property="og:image" content="https://thedroth.rocks/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E"><meta property="og:site_name" content="ExampleSite"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://thedroth.rocks/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E"><meta name=twitter:title content="404 Page not found"><meta name=twitter:description content="ExampleSite description"></head><body class=list id=top><script>localStorage.getItem("pref-theme")==="dark"?document.body.classList.add("dark"):localStorage.getItem("pref-theme")==="light"?document.body.classList.remove("dark"):window.matchMedia("(prefers-color-scheme: dark)").matches&&document.body.classList.add("dark")</script><header class=header><nav class=nav><div class=logo><a href=https://thedroth.rocks/ accesskey=h title="TheDroth.Rocks (Alt + H)"><img src=https://thedroth.rocks/images/thedroth_logo.png alt aria-label=logo height=35>TheDroth.Rocks</a><div class=logo-switches><button id=theme-toggle accesskey=t title="(Alt + T)"><svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/></svg><svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg></button></div></div><ul id=menu><li><a href=https://thedroth.rocks/our-services/ title="Наши сервисы"><span>Наши сервисы</span></a></li><li><a href=https://thedroth.rocks/proxy/ title="Проксирующие сервисы"><span>Проксирующие сервисы</span></a></li><li><a href=https://git.thedroth.rocks/TheDroth/ToS/src/branch/main/README.md title=ToS><span>ToS</span> <svg fill="none" shape-rendering="geometricPrecision" stroke="currentcolor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" viewBox="0 0 24 24" height="12" width="12"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><path d="M15 3h6v6"/><path d="M10 14 21 3"/></svg></a></li><li><a href=https://thedroth.rocks/blog/ title=Блог><span>Блог</span></a></li></ul></nav></header><main class=main><div class=not-found>404</div></main><footer class=footer><span>© 2023 <a href=https://thedroth.rocks/>TheDrothRocks!</a></span>
|
||||||
|
<span>Powered by
|
||||||
|
<a href=https://gohugo.io/ rel="noopener noreferrer" target=_blank>Hugo</a> &
|
||||||
|
<a href=https://github.com/adityatelange/hugo-PaperMod/ rel=noopener target=_blank>PaperMod</a></span></footer><a href=#top aria-label="go to top" title="Go to Top (Alt + G)" class=top-link id=top-link accesskey=g><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentcolor"><path d="M12 6H0l6-6z"/></svg></a><script>let menu=document.getElementById("menu");menu&&(menu.scrollLeft=localStorage.getItem("menu-scroll-position"),menu.onscroll=function(){localStorage.setItem("menu-scroll-position",menu.scrollLeft)}),document.querySelectorAll('a[href^="#"]').forEach(e=>{e.addEventListener("click",function(e){e.preventDefault();var t=this.getAttribute("href").substr(1);window.matchMedia("(prefers-reduced-motion: reduce)").matches?document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView():document.querySelector(`[id='${decodeURIComponent(t)}']`).scrollIntoView({behavior:"smooth"}),t==="top"?history.replaceState(null,null," "):history.pushState(null,null,`#${t}`)})})</script><script>var mybutton=document.getElementById("top-link");window.onscroll=function(){document.body.scrollTop>800||document.documentElement.scrollTop>800?(mybutton.style.visibility="visible",mybutton.style.opacity="1"):(mybutton.style.visibility="hidden",mybutton.style.opacity="0")}</script><script>document.getElementById("theme-toggle").addEventListener("click",()=>{document.body.className.includes("dark")?(document.body.classList.remove("dark"),localStorage.setItem("pref-theme","light")):(document.body.classList.add("dark"),localStorage.setItem("pref-theme","dark"))})</script></body></html>
|
4
public/categories/index.html
Normal file
14
public/categories/index.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
||||||
|
<channel>
|
||||||
|
<title>Categories on TheDrothRocks!</title>
|
||||||
|
<link>https://thedroth.rocks/categories/</link>
|
||||||
|
<description>Recent content in Categories on TheDrothRocks!</description>
|
||||||
|
<image>
|
||||||
|
<title>TheDrothRocks!</title>
|
||||||
|
<url>https://thedroth.rocks/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</url>
|
||||||
|
<link>https://thedroth.rocks/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</link>
|
||||||
|
</image>
|
||||||
|
<generator>Hugo -- gohugo.io</generator><atom:link href="https://thedroth.rocks/categories/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
</channel>
|
||||||
|
</rss>
|
46
public/core/index.xml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<title>Cores on TheDrothRocks!</title>
|
||||||
|
<link>https://thedroth.rocks/core/</link>
|
||||||
|
<description>Recent content in Cores on TheDrothRocks!</description>
|
||||||
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
|
<lastBuildDate>Tue, 15 Sep 2020 11:30:03 +0000</lastBuildDate><atom:link href="https://thedroth.rocks/core/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
<item>
|
||||||
|
<title>Наши сервисы</title>
|
||||||
|
<link>https://thedroth.rocks/core/our-services/</link>
|
||||||
|
<pubDate>Tue, 15 Sep 2020 11:30:03 +0000</pubDate>
|
||||||
|
|
||||||
|
<guid>https://thedroth.rocks/core/our-services/</guid>
|
||||||
|
<description>Данная страница - сборник ссылок на сервисы, которые хранятся на наших серверах. Все сервисы из данного списка &ldquo;подчинаяются&rdquo; нашему ToS, а потому настоятельно рекомендуем ознакомиться с ним перед началом взаимодействия с ресурсами.
|
||||||
|
Asocial Lemmy - федеративный форум-аггрегатор ссылок в стиле Reddit
|
||||||
|
TheДротский поиск SearXNG Метапоисковый сервис (поиск через Google, Bing, DuckDuckGo&hellip;)
|
||||||
|
Gitea Хранилище исходных кодов
|
||||||
|
RSS Bridge Конвертация новостных лент в RSS</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Проксирующие сервисы</title>
|
||||||
|
<link>https://thedroth.rocks/core/proxy/</link>
|
||||||
|
<pubDate>Tue, 15 Sep 2020 11:30:03 +0000</pubDate>
|
||||||
|
|
||||||
|
<guid>https://thedroth.rocks/core/proxy/</guid>
|
||||||
|
<description>Piped Лёгкий приватный интерфейс для YouTube, умеюший автоматически проматывать рекламные вставки. Зачем кормить гугл, если можно не кормить&hellip;
|
||||||
|
WikiLess Wikipedia без ненужных элементов и трекеров</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Donate!</title>
|
||||||
|
<link>https://thedroth.rocks/core/donate/</link>
|
||||||
|
<pubDate>Tue, 15 Sep 2020 11:30:03 +0000</pubDate>
|
||||||
|
|
||||||
|
<guid>https://thedroth.rocks/core/donate/</guid>
|
||||||
|
<description>Больше спасибо, что посетили данную страницу! Обращаем ваше внимание, что материальная поддержка вовсе не обязательно и вы можете пользоваться всеми нашими ресурсами абсолютно бесплатно!
|
||||||
|
Способы поддержки Единоразово Банковская карта: номер карты Донат с сообщением: Donationalerts, Boosty
|
||||||
|
Криптовалюта:
|
||||||
|
btc: номер кошелька Подписка Подписаться можно на Boosty
|
||||||
|
Зачем мне Вас поддерживать? </description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</channel>
|
||||||
|
</rss>
|
BIN
public/images/favicon.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
public/images/favicon_dithered.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
public/images/git_dithered.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
public/images/lemmy.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
BIN
public/images/piped_dithered.png
Normal file
After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
BIN
public/images/searx.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
public/images/thedroth_logo.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
4
public/index.html
Normal file
33
public/index.xml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
||||||
|
<channel>
|
||||||
|
<title>TheDrothRocks!</title>
|
||||||
|
<link>https://thedroth.rocks/</link>
|
||||||
|
<description>Recent content on TheDrothRocks!</description>
|
||||||
|
<image>
|
||||||
|
<title>TheDrothRocks!</title>
|
||||||
|
<url>https://thedroth.rocks/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</url>
|
||||||
|
<link>https://thedroth.rocks/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</link>
|
||||||
|
</image>
|
||||||
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
|
<lastBuildDate>Tue, 15 Sep 2020 11:30:03 +0000</lastBuildDate><atom:link href="https://thedroth.rocks/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
<item>
|
||||||
|
<title>Наши сервисы</title>
|
||||||
|
<link>https://thedroth.rocks/our-services/</link>
|
||||||
|
<pubDate>Tue, 15 Sep 2020 11:30:03 +0000</pubDate>
|
||||||
|
|
||||||
|
<guid>https://thedroth.rocks/our-services/</guid>
|
||||||
|
<description>Наши собственные сервисы. Заголовки - ссылки.</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<title>Проксирующие сервисы</title>
|
||||||
|
<link>https://thedroth.rocks/proxy/</link>
|
||||||
|
<pubDate>Tue, 15 Sep 2020 11:30:03 +0000</pubDate>
|
||||||
|
|
||||||
|
<guid>https://thedroth.rocks/proxy/</guid>
|
||||||
|
<description>Все сервисы, которые мы проксируем. Заголовки - ссылки.</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</channel>
|
||||||
|
</rss>
|
5
public/our-services/index.html
Normal file
10
public/our-services/index.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||||
|
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||||
|
<channel>
|
||||||
|
<title>Tags on My New Hugo Site</title>
|
||||||
|
<link>http://example.org/tags/</link>
|
||||||
|
<description>Recent content in Tags on My New Hugo Site</description>
|
||||||
|
<generator>Hugo -- gohugo.io</generator>
|
||||||
|
<language>en-us</language><atom:link href="http://example.org/tags/index.xml" rel="self" type="application/rss+xml" />
|
||||||
|
</channel>
|
||||||
|
</rss>
|