mirror of
https://github.com/YACReader/yacreader
synced 2026-04-12 15:49:53 -04:00
Make flow ribbons themeable
This commit is contained in:
@ -24,6 +24,7 @@ void ComicFlowWidget::applyTheme(const Theme &theme)
|
|||||||
{
|
{
|
||||||
setBackgroundColor(theme.comicFlow.backgroundColor);
|
setBackgroundColor(theme.comicFlow.backgroundColor);
|
||||||
setTextColor(theme.comicFlow.textColor);
|
setTextColor(theme.comicFlow.textColor);
|
||||||
|
flow->setRibbonImages(theme.comicFlow.readPixmap.toImage(), theme.comicFlow.readingPixmap.toImage());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComicFlowWidget::setBackgroundColor(const QColor &color)
|
void ComicFlowWidget::setBackgroundColor(const QColor &color)
|
||||||
|
|||||||
@ -78,8 +78,8 @@
|
|||||||
<file>../images/notCover.png</file>
|
<file>../images/notCover.png</file>
|
||||||
<file>../images/library_dialogs/openLibrary.svg</file>
|
<file>../images/library_dialogs/openLibrary.svg</file>
|
||||||
<file>../images/metadata_dialog/previousCoverPage.svg</file>
|
<file>../images/metadata_dialog/previousCoverPage.svg</file>
|
||||||
<file>../images/readingRibbon.png</file>
|
<file>../images/readingRibbon.svg</file>
|
||||||
<file>../images/readRibbon.png</file>
|
<file>../images/readRibbon.svg</file>
|
||||||
<file>../images/metadata_dialog/resetCover.svg</file>
|
<file>../images/metadata_dialog/resetCover.svg</file>
|
||||||
<file>../images/search_result.svg</file>
|
<file>../images/search_result.svg</file>
|
||||||
<file>../images/serverConfigBackground.svg</file>
|
<file>../images/serverConfigBackground.svg</file>
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"comicFlow": {
|
"comicFlow": {
|
||||||
"backgroundColor": "#000000",
|
"backgroundColor": "#000000",
|
||||||
|
"readMainColor": "#db4725",
|
||||||
|
"readTickColor": "#8a2c17",
|
||||||
|
"readingColor": "#e6b90f",
|
||||||
"textColor": "#4c4c4c"
|
"textColor": "#4c4c4c"
|
||||||
},
|
},
|
||||||
"comicsViewTable": {
|
"comicsViewTable": {
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"comicFlow": {
|
"comicFlow": {
|
||||||
"backgroundColor": "#111111",
|
"backgroundColor": "#111111",
|
||||||
|
"readMainColor": "#db4725",
|
||||||
|
"readTickColor": "#8a2c17",
|
||||||
|
"readingColor": "#e6b90f",
|
||||||
"textColor": "#888888"
|
"textColor": "#888888"
|
||||||
},
|
},
|
||||||
"comicsViewTable": {
|
"comicsViewTable": {
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
{
|
{
|
||||||
"comicFlow": {
|
"comicFlow": {
|
||||||
"backgroundColor": "#dcdcdc",
|
"backgroundColor": "#dcdcdc",
|
||||||
|
"readMainColor": "#db4725",
|
||||||
|
"readTickColor": "#8a2c17",
|
||||||
|
"readingColor": "#e6b90f",
|
||||||
"textColor": "#303030"
|
"textColor": "#303030"
|
||||||
},
|
},
|
||||||
"comicsViewTable": {
|
"comicsViewTable": {
|
||||||
|
|||||||
@ -92,9 +92,11 @@ struct MetadataScraperDialogThemeTemplates {
|
|||||||
QSize rowIconSize = QSize(8, 7);
|
QSize rowIconSize = QSize(8, 7);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ComicFlowColors {
|
struct ComicFlowTheme {
|
||||||
QColor backgroundColor;
|
QColor backgroundColor;
|
||||||
QColor textColor;
|
QColor textColor;
|
||||||
|
QPixmap readPixmap;
|
||||||
|
QPixmap readingPixmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ComicsViewTableThemeTemplates {
|
struct ComicsViewTableThemeTemplates {
|
||||||
@ -484,7 +486,7 @@ struct Theme {
|
|||||||
ThemeMeta meta;
|
ThemeMeta meta;
|
||||||
QJsonObject sourceJson;
|
QJsonObject sourceJson;
|
||||||
|
|
||||||
ComicFlowColors comicFlow;
|
ComicFlowTheme comicFlow;
|
||||||
MetadataScraperDialogTheme metadataScraperDialog;
|
MetadataScraperDialogTheme metadataScraperDialog;
|
||||||
HelpAboutDialogTheme helpAboutDialog;
|
HelpAboutDialogTheme helpAboutDialog;
|
||||||
WhatsNewDialogTheme whatsNewDialog;
|
WhatsNewDialogTheme whatsNewDialog;
|
||||||
|
|||||||
@ -330,10 +330,18 @@ struct WhatsNewDialogParams {
|
|||||||
QColor headerDecorationColor;
|
QColor headerDecorationColor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ComicFlowParams {
|
||||||
|
QColor backgroundColor;
|
||||||
|
QColor textColor;
|
||||||
|
QColor readMainColor; // Main ribbon color for read state (#f0f in readRibbon.svg)
|
||||||
|
QColor readTickColor; // Tick color for read state (#0ff in readRibbon.svg)
|
||||||
|
QColor readingColor; // Main ribbon color for reading state (#f0f in readingRibbon.svg)
|
||||||
|
};
|
||||||
|
|
||||||
struct ThemeParams {
|
struct ThemeParams {
|
||||||
ThemeMeta meta;
|
ThemeMeta meta;
|
||||||
|
|
||||||
ComicFlowColors comicFlowColors;
|
ComicFlowParams comicFlowParams;
|
||||||
MetadataScraperDialogParams metadataScraperDialogParams;
|
MetadataScraperDialogParams metadataScraperDialogParams;
|
||||||
HelpAboutDialogTheme helpAboutDialogParams;
|
HelpAboutDialogTheme helpAboutDialogParams;
|
||||||
EmptyContainerParams emptyContainerParams;
|
EmptyContainerParams emptyContainerParams;
|
||||||
@ -361,10 +369,22 @@ Theme makeTheme(const ThemeParams ¶ms)
|
|||||||
Theme theme;
|
Theme theme;
|
||||||
|
|
||||||
// Comic Flow
|
// Comic Flow
|
||||||
const auto &cf = params.comicFlowColors;
|
const auto &cf = params.comicFlowParams;
|
||||||
theme.comicFlow.backgroundColor = cf.backgroundColor;
|
theme.comicFlow.backgroundColor = cf.backgroundColor;
|
||||||
theme.comicFlow.textColor = cf.textColor;
|
theme.comicFlow.textColor = cf.textColor;
|
||||||
|
|
||||||
|
{
|
||||||
|
const qreal dpr = qApp->devicePixelRatio();
|
||||||
|
// readRibbon: #f0f (main) + #0ff (tick)
|
||||||
|
theme.comicFlow.readPixmap = renderSvgToPixmap(
|
||||||
|
recoloredSvgToThemeFile(":/images/readRibbon.svg", cf.readMainColor, cf.readTickColor, params.meta.id),
|
||||||
|
100, 136, dpr);
|
||||||
|
// readingRibbon: #f0f (main)
|
||||||
|
theme.comicFlow.readingPixmap = renderSvgToPixmap(
|
||||||
|
recoloredSvgToThemeFile(":/images/readingRibbon.svg", cf.readingColor, params.meta.id),
|
||||||
|
100, 136, dpr);
|
||||||
|
}
|
||||||
|
|
||||||
// MetadataScraperDialog
|
// MetadataScraperDialog
|
||||||
const auto &msd = params.metadataScraperDialogParams;
|
const auto &msd = params.metadataScraperDialogParams;
|
||||||
const auto &t = msd.t;
|
const auto &t = msd.t;
|
||||||
@ -918,8 +938,11 @@ Theme makeTheme(const QJsonObject &json)
|
|||||||
|
|
||||||
if (json.contains("comicFlow")) {
|
if (json.contains("comicFlow")) {
|
||||||
const auto o = json["comicFlow"].toObject();
|
const auto o = json["comicFlow"].toObject();
|
||||||
p.comicFlowColors.backgroundColor = colorFromJson(o, "backgroundColor", p.comicFlowColors.backgroundColor);
|
p.comicFlowParams.backgroundColor = colorFromJson(o, "backgroundColor", p.comicFlowParams.backgroundColor);
|
||||||
p.comicFlowColors.textColor = colorFromJson(o, "textColor", p.comicFlowColors.textColor);
|
p.comicFlowParams.textColor = colorFromJson(o, "textColor", p.comicFlowParams.textColor);
|
||||||
|
p.comicFlowParams.readMainColor = colorFromJson(o, "readMainColor", p.comicFlowParams.readMainColor);
|
||||||
|
p.comicFlowParams.readTickColor = colorFromJson(o, "readTickColor", p.comicFlowParams.readTickColor);
|
||||||
|
p.comicFlowParams.readingColor = colorFromJson(o, "readingColor", p.comicFlowParams.readingColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.contains("metadataScraperDialog")) {
|
if (json.contains("metadataScraperDialog")) {
|
||||||
|
|||||||
@ -138,28 +138,8 @@ void YACReaderFlow3D::initialize(QRhiCommandBuffer *cb)
|
|||||||
qDebug() << "YACReaderFlow3D: Created defaultTexture" << defaultImage.size();
|
qDebug() << "YACReaderFlow3D: Created defaultTexture" << defaultImage.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef YACREADER_LIBRARY
|
if (ribbonTexturesDirty || (!readRibbonImage.isNull() && !scene.markTexture) || (!readingRibbonImage.isNull() && !scene.readingTexture))
|
||||||
// Initialize mark textures
|
syncRibbonTextures(getResourceBatch());
|
||||||
if (!scene.markTexture) {
|
|
||||||
QImage markImage = QImage(":/images/readRibbon.png").convertToFormat(QImage::Format_RGBA8888);
|
|
||||||
if (!markImage.isNull()) {
|
|
||||||
scene.markTexture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, markImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
|
|
||||||
scene.markTexture->create();
|
|
||||||
getResourceBatch()->uploadTexture(scene.markTexture.get(), markImage);
|
|
||||||
getResourceBatch()->generateMips(scene.markTexture.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!scene.readingTexture) {
|
|
||||||
QImage readingImage = QImage(":/images/readingRibbon.png").convertToFormat(QImage::Format_RGBA8888);
|
|
||||||
if (!readingImage.isNull()) {
|
|
||||||
scene.readingTexture.reset(m_rhi->newTexture(QRhiTexture::RGBA8, readingImage.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
|
|
||||||
scene.readingTexture->create();
|
|
||||||
getResourceBatch()->uploadTexture(scene.readingTexture.get(), readingImage);
|
|
||||||
getResourceBatch()->generateMips(scene.readingTexture.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Create vertex buffer (quad geometry)
|
// Create vertex buffer (quad geometry)
|
||||||
if (!scene.vertexBuffer) {
|
if (!scene.vertexBuffer) {
|
||||||
@ -373,9 +353,35 @@ void YACReaderFlow3D::ensurePipeline()
|
|||||||
|
|
||||||
void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
||||||
{
|
{
|
||||||
if (!m_rhi || numObjects == 0)
|
if (!m_rhi)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
QRhiResourceUpdateBatch *batch = scene.resourceUpdates;
|
||||||
|
scene.resourceUpdates = nullptr;
|
||||||
|
|
||||||
|
auto ensureBatch = [this, &batch]() {
|
||||||
|
if (!batch)
|
||||||
|
batch = m_rhi->nextResourceUpdateBatch();
|
||||||
|
return batch;
|
||||||
|
};
|
||||||
|
auto deferBatch = [this, &batch]() {
|
||||||
|
if (batch)
|
||||||
|
scene.resourceUpdates = batch;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef YACREADER_LIBRARY
|
||||||
|
if (ribbonTexturesDirty || (!readRibbonImage.isNull() && !scene.markTexture) || (!readingRibbonImage.isNull() && !scene.readingTexture))
|
||||||
|
syncRibbonTextures(ensureBatch());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Even without draw calls, pending uploads still have to reach the GPU.
|
||||||
|
// Otherwise recreated ribbon textures would exist but never receive pixels.
|
||||||
|
if (numObjects == 0) {
|
||||||
|
if (batch)
|
||||||
|
cb->resourceUpdate(batch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const QSize outputSize = renderTarget()->pixelSize();
|
const QSize outputSize = renderTarget()->pixelSize();
|
||||||
const QColor clearColor = backgroundColor;
|
const QColor clearColor = backgroundColor;
|
||||||
|
|
||||||
@ -552,6 +558,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
ensureUniformBufferCapacity(draws.size());
|
ensureUniformBufferCapacity(draws.size());
|
||||||
|
|
||||||
if (!scene.uniformBuffer) {
|
if (!scene.uniformBuffer) {
|
||||||
|
deferBatch();
|
||||||
qWarning() << "YACReaderFlow3D: No uniform buffer available for rendering";
|
qWarning() << "YACReaderFlow3D: No uniform buffer available for rendering";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -560,6 +567,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
ensurePipeline();
|
ensurePipeline();
|
||||||
|
|
||||||
if (!scene.pipeline) {
|
if (!scene.pipeline) {
|
||||||
|
deferBatch();
|
||||||
qWarning() << "YACReaderFlow3D: No pipeline available for rendering";
|
qWarning() << "YACReaderFlow3D: No pipeline available for rendering";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -569,6 +577,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
if (!scene.instanceBuffer || scene.instanceBuffer->size() < requiredInstanceSize) {
|
if (!scene.instanceBuffer || scene.instanceBuffer->size() < requiredInstanceSize) {
|
||||||
scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, requiredInstanceSize));
|
scene.instanceBuffer.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, requiredInstanceSize));
|
||||||
if (!scene.instanceBuffer->create()) {
|
if (!scene.instanceBuffer->create()) {
|
||||||
|
deferBatch();
|
||||||
qWarning() << "YACReaderFlow3D: Failed to create instance buffer of size" << requiredInstanceSize;
|
qWarning() << "YACReaderFlow3D: Failed to create instance buffer of size" << requiredInstanceSize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -576,7 +585,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
// === PHASE 1: PREPARE (BEFORE PASS) ===
|
// === PHASE 1: PREPARE (BEFORE PASS) ===
|
||||||
// Update ALL uniform and instance data for ALL draws in one batch
|
// Update ALL uniform and instance data for ALL draws in one batch
|
||||||
QRhiResourceUpdateBatch *batch = m_rhi->nextResourceUpdateBatch();
|
batch = ensureBatch();
|
||||||
|
|
||||||
// Process pending texture uploads
|
// Process pending texture uploads
|
||||||
if (!pendingTextureUploads.isEmpty()) {
|
if (!pendingTextureUploads.isEmpty()) {
|
||||||
@ -603,6 +612,7 @@ void YACReaderFlow3D::render(QRhiCommandBuffer *cb)
|
|||||||
|
|
||||||
// === PHASE 2: RENDER (DURING PASS) ===
|
// === PHASE 2: RENDER (DURING PASS) ===
|
||||||
cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, batch);
|
cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, batch);
|
||||||
|
batch = nullptr;
|
||||||
|
|
||||||
cb->setGraphicsPipeline(scene.pipeline.get());
|
cb->setGraphicsPipeline(scene.pipeline.get());
|
||||||
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
|
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
|
||||||
@ -762,6 +772,65 @@ void YACReaderFlow3D::executeDrawWithOffset(QRhiCommandBuffer *cb, QRhiTexture *
|
|||||||
cb->draw(6);
|
cb->draw(6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void YACReaderFlow3D::removeCachedShaderBindings(QRhiTexture *texture)
|
||||||
|
{
|
||||||
|
if (!texture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = scene.shaderBindingsCache.find(texture);
|
||||||
|
if (it != scene.shaderBindingsCache.end()) {
|
||||||
|
delete it.value();
|
||||||
|
scene.shaderBindingsCache.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void YACReaderFlow3D::syncRibbonTextures(QRhiResourceUpdateBatch *batch)
|
||||||
|
{
|
||||||
|
#ifndef YACREADER_LIBRARY
|
||||||
|
Q_UNUSED(batch);
|
||||||
|
ribbonTexturesDirty = false;
|
||||||
|
#else
|
||||||
|
if (!m_rhi || !batch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool allTexturesReady = true;
|
||||||
|
|
||||||
|
auto syncTexture = [this, batch, &allTexturesReady](std::unique_ptr<QRhiTexture> &texture, const QImage &image, const char *name) {
|
||||||
|
if (image.isNull()) {
|
||||||
|
if (texture) {
|
||||||
|
removeCachedShaderBindings(texture.get());
|
||||||
|
texture.reset();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture && !ribbonTexturesDirty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (texture) {
|
||||||
|
removeCachedShaderBindings(texture.get());
|
||||||
|
texture.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<QRhiTexture> newTexture(m_rhi->newTexture(QRhiTexture::RGBA8, image.size(), 1, QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips));
|
||||||
|
if (!newTexture->create()) {
|
||||||
|
qWarning() << "YACReaderFlow3D: Failed to create" << name << "ribbon texture";
|
||||||
|
allTexturesReady = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
batch->uploadTexture(newTexture.get(), image);
|
||||||
|
batch->generateMips(newTexture.get());
|
||||||
|
texture = std::move(newTexture);
|
||||||
|
};
|
||||||
|
|
||||||
|
syncTexture(scene.markTexture, readRibbonImage, "read");
|
||||||
|
syncTexture(scene.readingTexture, readingRibbonImage, "reading");
|
||||||
|
|
||||||
|
ribbonTexturesDirty = !allTexturesReady;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void YACReaderFlow3D::releaseResources()
|
void YACReaderFlow3D::releaseResources()
|
||||||
{
|
{
|
||||||
scene.reset();
|
scene.reset();
|
||||||
@ -1278,6 +1347,19 @@ void YACReaderFlow3D::setTextColor(const QColor &color)
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void YACReaderFlow3D::setRibbonImages(const QImage &readImage, const QImage &readingImage)
|
||||||
|
{
|
||||||
|
readRibbonImage = readImage.convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
readingRibbonImage = readingImage.convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
#ifdef YACREADER_LIBRARY
|
||||||
|
ribbonTexturesDirty = true;
|
||||||
|
#else
|
||||||
|
ribbonTexturesDirty = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
void YACReaderFlow3D::wheelEvent(QWheelEvent *event)
|
void YACReaderFlow3D::wheelEvent(QWheelEvent *event)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -179,6 +179,10 @@ protected:
|
|||||||
QColor backgroundColor;
|
QColor backgroundColor;
|
||||||
QColor textColor;
|
QColor textColor;
|
||||||
|
|
||||||
|
QImage readRibbonImage;
|
||||||
|
QImage readingRibbonImage;
|
||||||
|
bool ribbonTexturesDirty = false;
|
||||||
|
|
||||||
/*** System info ***/
|
/*** System info ***/
|
||||||
float viewRotate;
|
float viewRotate;
|
||||||
|
|
||||||
@ -202,6 +206,8 @@ protected:
|
|||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
QRhiTexture *createTextureFromImage(QRhiCommandBuffer *cb, const QImage &image);
|
QRhiTexture *createTextureFromImage(QRhiCommandBuffer *cb, const QImage &image);
|
||||||
|
void removeCachedShaderBindings(QRhiTexture *texture);
|
||||||
|
void syncRibbonTextures(QRhiResourceUpdateBatch *batch);
|
||||||
void updateUniformBuffer(QRhiCommandBuffer *cb, const UniformData &data);
|
void updateUniformBuffer(QRhiCommandBuffer *cb, const UniformData &data);
|
||||||
void prepareMarkInstanceData(const YACReader3DImageRHI &image, QVector<float> &data);
|
void prepareMarkInstanceData(const YACReader3DImageRHI &image, QVector<float> &data);
|
||||||
void ensureUniformBufferCapacity(int requiredSlots);
|
void ensureUniformBufferCapacity(int requiredSlots);
|
||||||
@ -260,6 +266,9 @@ public slots:
|
|||||||
void setBackgroundColor(const QColor &color);
|
void setBackgroundColor(const QColor &color);
|
||||||
void setTextColor(const QColor &color);
|
void setTextColor(const QColor &color);
|
||||||
|
|
||||||
|
// Ribbon image setters (for themed SVG rasterized images)
|
||||||
|
void setRibbonImages(const QImage &readImage, const QImage &readingImage);
|
||||||
|
|
||||||
virtual void updateImageData() = 0;
|
virtual void updateImageData() = 0;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 KiB |
24
images/readRibbon.svg
Normal file
24
images/readRibbon.svg
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 136">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #0ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cls-2 {
|
||||||
|
fill: #f0f;
|
||||||
|
filter: url(#drop-shadow-1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<filter id="drop-shadow-1" x="0" y="-1" width="100" height="134" filterUnits="userSpaceOnUse">
|
||||||
|
<feOffset dx="0" dy="1"/>
|
||||||
|
<feGaussianBlur result="blur" stdDeviation="2"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".33"/>
|
||||||
|
<feComposite in2="blur" operator="in"/>
|
||||||
|
<feComposite in="SourceGraphic"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-2" d="M90.69,125.14l-38.73-33.45c-1.13-.97-2.8-.97-3.92,0L9.31,125.14c-1.3,1.12-3.31.2-3.31-1.51V8c0-2.21,1.79-4,4-4h80c2.21,0,4,1.79,4,4v115.63c0,1.71-2.01,2.63-3.31,1.51Z"/>
|
||||||
|
<polygon class="cls-1" points="34.29 52 40 46.29 46 52.29 61 37.29 66.71 43 46 63.71 34.29 52"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 988 B |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
19
images/readingRibbon.svg
Normal file
19
images/readingRibbon.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 136">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #f0f;
|
||||||
|
filter: url(#drop-shadow-2);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<filter id="drop-shadow-2" x="0" y="-1" width="100" height="134" filterUnits="userSpaceOnUse">
|
||||||
|
<feOffset dx="0" dy="1"/>
|
||||||
|
<feGaussianBlur result="blur" stdDeviation="2"/>
|
||||||
|
<feFlood flood-color="#000" flood-opacity=".33"/>
|
||||||
|
<feComposite in2="blur" operator="in"/>
|
||||||
|
<feComposite in="SourceGraphic"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<path class="cls-1" d="M90.69,125.14l-38.73-33.45c-1.13-.97-2.8-.97-3.92,0L9.31,125.14c-1.3,1.12-3.31.2-3.31-1.51V8c0-2.21,1.79-4,4-4h80c2.21,0,4,1.79,4,4v115.63c0,1.71-2.01,2.63-3.31,1.51Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 845 B |
Reference in New Issue
Block a user