🌈 Color histograms for images, along with stats
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
import React, { ReactElement, useCallback, useState } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { DnD } from "../../DnD";
|
||||
import { isEmpty } from "lodash";
|
||||
import { isEmpty, isNil, isUndefined } from "lodash";
|
||||
import Sticky from "react-stickynode";
|
||||
import SlidingPane from "react-sliding-pane";
|
||||
import { extractComicArchive } from "../../../actions/fileops.actions";
|
||||
import { analyzeImage } from "../../../actions/fileops.actions";
|
||||
import { Canvas } from "../../shared/Canvas";
|
||||
|
||||
export const ArchiveOperations = (props): ReactElement => {
|
||||
const { data } = props;
|
||||
@@ -16,9 +17,9 @@ export const ArchiveOperations = (props): ReactElement => {
|
||||
(state: RootState) => state.fileOps.extractedComicBookArchive,
|
||||
);
|
||||
|
||||
const imageAnalysisResult = useSelector(
|
||||
(state: RootState) => state.fileOps.imageAnalysisResults,
|
||||
);
|
||||
const imageAnalysisResult = useSelector((state: RootState) => {
|
||||
return state.fileOps.imageAnalysisResults;
|
||||
});
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const unpackComicArchive = useCallback(() => {
|
||||
@@ -44,9 +45,14 @@ export const ArchiveOperations = (props): ReactElement => {
|
||||
content: () => {
|
||||
return (
|
||||
<div>
|
||||
<pre>{currentImage}</pre>
|
||||
<pre className="is-size-7">
|
||||
{JSON.stringify(imageAnalysisResult, null, 2)}
|
||||
<pre className="is-size-7">{currentImage}</pre>
|
||||
{!isEmpty(imageAnalysisResult) ? (
|
||||
<pre className="is-size-7 p-2 mt-3">
|
||||
<Canvas data={imageAnalysisResult} />
|
||||
</pre>
|
||||
) : null}
|
||||
<pre className="is-size-7 mt-3">
|
||||
{JSON.stringify(imageAnalysisResult.analyzedData, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -32,6 +32,28 @@ export const Dashboard = (): ReactElement => {
|
||||
|
||||
{!isEmpty(recentComics) && !isEmpty(recentComics.docs) ? (
|
||||
<>
|
||||
{/* stats */}
|
||||
<div>
|
||||
<div className="box stats-palette p-3 column is-one-quarter">
|
||||
<dl>
|
||||
<dd className="is-size-4">
|
||||
<span className="has-text-weight-bold">113123</span> files
|
||||
</dd>
|
||||
<dd className="is-size-6">
|
||||
<span className="has-text-weight-bold">140</span> tagged
|
||||
with ComicVine
|
||||
</dd>
|
||||
<dd className="is-size-6">
|
||||
<span className="has-text-weight-bold">1304</span> with
|
||||
custom metadata
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="box stats-palette p-3 column ml-5 is-one-quarter">
|
||||
asdasd
|
||||
</div>
|
||||
</div>
|
||||
<RecentlyImported comicBookCovers={recentComics} />
|
||||
{!isNil(volumeGroups) ? <VolumeGroups /> : null}
|
||||
</>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Link } from "react-router-dom";
|
||||
export const SearchBar = (): ReactElement => {
|
||||
const foo = () => {};
|
||||
return (
|
||||
<div className="box columns sticky">
|
||||
<div className="box sticky">
|
||||
<Form
|
||||
onSubmit={foo}
|
||||
initialValues={{}}
|
||||
|
||||
@@ -47,8 +47,9 @@ export const LibraryGrid = (libraryGridProps: ILibraryGridProps) => {
|
||||
let comicName = "";
|
||||
if (!isNil(rawFileDetails)) {
|
||||
const encodedFilePath = encodeURI(
|
||||
`${LIBRARY_SERVICE_HOST}` +
|
||||
removeLeadingPeriod(rawFileDetails.cover.filePath),
|
||||
`${LIBRARY_SERVICE_HOST}/${removeLeadingPeriod(
|
||||
rawFileDetails.cover.filePath,
|
||||
)}`,
|
||||
);
|
||||
imagePath = escapePoundSymbol(encodedFilePath);
|
||||
comicName = rawFileDetails.name;
|
||||
|
||||
64
src/client/components/shared/Canvas.tsx
Normal file
64
src/client/components/shared/Canvas.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
|
||||
export const Canvas = (data) => {
|
||||
const { colorHistogramData } = data.data;
|
||||
console.log(data);
|
||||
const width = 559;
|
||||
const height = 200;
|
||||
const pixelRatio = window.devicePixelRatio;
|
||||
|
||||
const canvas = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
const context = canvas.current.getContext("2d");
|
||||
|
||||
const guideHeight = 8;
|
||||
const startY = height - guideHeight;
|
||||
const dx = width / 256;
|
||||
const dy = startY / colorHistogramData.maxBrightness;
|
||||
context.lineWidth = dx;
|
||||
context.fillStyle = "transparent";
|
||||
context.fillRect(0, 0, width, height);
|
||||
|
||||
for (let i = 0; i < 256; i++) {
|
||||
const x = i * dx;
|
||||
|
||||
// Red
|
||||
context.strokeStyle = "rgba(220,0,0,0.5)";
|
||||
context.beginPath();
|
||||
context.moveTo(x, startY);
|
||||
context.lineTo(x, startY - colorHistogramData.r[i] * dy);
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
// Green
|
||||
context.strokeStyle = "rgba(0,210,0,0.5)";
|
||||
context.beginPath();
|
||||
context.moveTo(x, startY);
|
||||
context.lineTo(x, startY - colorHistogramData.g[i] * dy);
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
// Blue
|
||||
context.strokeStyle = "rgba(0,0,255,0.5)";
|
||||
context.beginPath();
|
||||
context.moveTo(x, startY);
|
||||
context.lineTo(x, startY - colorHistogramData.b[i] * dy);
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
|
||||
// Guide
|
||||
context.strokeStyle = "rgb(" + i + ", " + i + ", " + i + ")";
|
||||
context.beginPath();
|
||||
context.moveTo(x, startY);
|
||||
context.lineTo(x, height);
|
||||
context.closePath();
|
||||
context.stroke();
|
||||
}
|
||||
});
|
||||
|
||||
const dw = Math.floor(pixelRatio * width);
|
||||
const dh = Math.floor(pixelRatio * height);
|
||||
const style = { width, height };
|
||||
return <canvas ref={canvas} width={dw} height={dh} style={style} />;
|
||||
};
|
||||
|
||||
export default Canvas;
|
||||
Reference in New Issue
Block a user