paintplus: vendor the app source and rename from EditmaskwithAI

Bring the full EditmaskwithAI application into the repo under paintplus/
(429 files) so the service is self-contained — the installer copies the
vendored source to ~/docker/paintplus/src instead of cloning at runtime.

Rename to PaintPlus (service + branding; app logic untouched):
- services/editmaskwithai.sh -> services/paintplus.sh (register_service
  paintplus, install_paintplus, ~/docker/paintplus, Caddy paintplus:8000,
  Authelia option preserved)
- container names -> paintplus across docker-compose*.yml; dev network
  -> paintplus-network
- browser <title> -> "PaintPlus - AI Image Editor"; README heading ->
  PaintPlus with upstream provenance note
- README utilities table: editmaskwithai -> paintplus

Backend/frontend code (help strings referencing the old container name,
the ai_photo_edit.db filename) is intentionally left as-is to avoid
touching application logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nb2vJ8W7bHKx1JXVvpCraH
This commit is contained in:
Claude
2026-06-26 05:48:43 +00:00
parent b4e8ba2a79
commit 084922afaa
431 changed files with 87396 additions and 77 deletions
@@ -0,0 +1,133 @@
/* ImageCanvas fills the entire canvas wrapper */
.canvas-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: hidden;
}
.canvas-container canvas {
display: block;
}
.canvas-controls {
position: absolute;
bottom: 10px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
pointer-events: none;
}
.canvas-controls > * {
pointer-events: auto;
}
.clear-selection-btn {
background-color: #ff4444;
color: white;
padding: 8px 16px;
border-radius: 4px;
}
.clear-selection-btn:hover {
background-color: #cc0000;
}
.selection-hint {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.8);
color: #0088ff;
padding: 6px 12px;
border-radius: 4px;
font-size: 11px;
z-index: 10;
white-space: nowrap;
pointer-events: none;
}
/* Clear selection button */
.clear-selection-btn {
position: absolute;
top: 8px;
right: 8px;
background-color: #cc3333;
color: white;
padding: 6px 12px;
font-size: 11px;
border: none;
border-radius: 3px;
cursor: pointer;
z-index: 10;
}
.clear-selection-btn:hover {
background-color: #dd4444;
}
/* Zoom controls */
.zoom-controls {
display: flex;
align-items: center;
gap: 4px;
background-color: rgba(0, 0, 0, 0.8);
padding: 6px 10px;
border-radius: 4px;
}
.zoom-controls button {
width: 28px;
height: 28px;
padding: 0;
font-size: 18px;
font-weight: bold;
background-color: #444;
color: white;
border: 1px solid #666;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.zoom-controls button:hover {
background-color: #555;
}
.zoom-level {
color: white;
font-size: 12px;
min-width: 45px;
text-align: center;
}
/* Tool mode indicator */
.tool-mode-indicator {
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 120, 255, 0.9);
color: white;
padding: 10px 20px;
border-radius: 4px;
font-size: 14px;
font-weight: 500;
z-index: 10;
white-space: nowrap;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
@@ -0,0 +1,752 @@
import React, { useEffect, useRef, useState, useCallback, forwardRef, useImperativeHandle } from 'react';
import { fabric } from 'fabric';
import './ImageCanvas.css';
const ImageCanvas = forwardRef(({
imageUrl,
onSelectionChange,
selectionMode,
advancedToolMode,
onAdvancedToolClick,
zoom = 100,
onZoomChange,
externalSelection,
isProcessing
}, ref) => {
const canvasRef = useRef(null);
const fabricCanvasRef = useRef(null);
const [currentSelection, setCurrentSelection] = useState(null);
const [currentZoom, setCurrentZoom] = useState(1);
const currentSelectionRef = useRef(null);
const lassoPoints = useRef([]);
const onZoomChangeRef = useRef(onZoomChange);
const imageRef = useRef(null);
const baseScaleRef = useRef(1);
const isDrawingRef = useRef(false);
// Keep ref updated
useEffect(() => {
onZoomChangeRef.current = onZoomChange;
}, [onZoomChange]);
// Expose methods to parent
useImperativeHandle(ref, () => ({
getCanvas: () => fabricCanvasRef.current,
clearSelection: () => clearSelection(),
}));
// Update selection ref when state changes
useEffect(() => {
currentSelectionRef.current = currentSelection;
}, [currentSelection]);
// Initialize canvas
useEffect(() => {
if (!canvasRef.current) return;
const canvas = new fabric.Canvas(canvasRef.current, {
selection: false,
backgroundColor: 'transparent',
preserveObjectStacking: true,
});
fabricCanvasRef.current = canvas;
const handleResize = () => {
const container = canvasRef.current?.parentElement;
if (container) {
const width = container.clientWidth;
const height = container.clientHeight;
canvas.setWidth(width);
canvas.setHeight(height);
// Re-center image if it exists
if (imageRef.current) {
centerImage(canvas, imageRef.current, zoom / 100);
}
canvas.renderAll();
}
};
// Initial resize - use requestAnimationFrame to ensure DOM is ready
requestAnimationFrame(() => {
handleResize();
});
window.addEventListener('resize', handleResize);
// Mouse wheel zoom
const handleWheel = (opt) => {
const e = opt.e;
e.preventDefault();
e.stopPropagation();
const delta = e.deltaY;
let newZoom = canvas.getZoom();
newZoom *= 0.999 ** delta;
// Clamp zoom between 0.1x and 10x
if (newZoom > 10) newZoom = 10;
if (newZoom < 0.1) newZoom = 0.1;
// Zoom to point under cursor
const pointer = canvas.getPointer(e, true);
canvas.zoomToPoint({ x: pointer.x, y: pointer.y }, newZoom);
setCurrentZoom(newZoom);
if (onZoomChangeRef.current) {
onZoomChangeRef.current(newZoom);
}
};
canvas.on('mouse:wheel', handleWheel);
return () => {
window.removeEventListener('resize', handleResize);
canvas.off('mouse:wheel', handleWheel);
canvas.dispose();
};
}, []); // Empty dependency array - only run once on mount
// Center and scale image
const centerImage = (canvas, img, zoomFactor) => {
if (!img) return;
const padding = 40;
const availableWidth = canvas.width - padding;
const availableHeight = canvas.height - padding;
// Calculate base scale to fit
const fitScale = Math.min(
availableWidth / img.width,
availableHeight / img.height
);
baseScaleRef.current = fitScale;
const scale = fitScale * zoomFactor;
img.scale(scale);
img.set({
left: (canvas.width - img.width * scale) / 2,
top: (canvas.height - img.height * scale) / 2,
});
};
// Apply zoom changes
useEffect(() => {
const canvas = fabricCanvasRef.current;
if (!canvas || !imageRef.current) return;
centerImage(canvas, imageRef.current, zoom / 100);
canvas.renderAll();
}, [zoom]);
// Load image when URL changes
useEffect(() => {
if (!fabricCanvasRef.current || !imageUrl) return;
const canvas = fabricCanvasRef.current;
// Ensure canvas has dimensions before loading image
if (canvas.width === 0 || canvas.height === 0) {
const container = canvasRef.current?.parentElement;
if (container) {
canvas.setWidth(container.clientWidth || 800);
canvas.setHeight(container.clientHeight || 600);
}
}
// Add cache buster to force reload
const cacheBustedUrl = `${imageUrl}?t=${Date.now()}`;
fabric.Image.fromURL(cacheBustedUrl, (img) => {
if (!img) {
console.error('Failed to load image from URL:', cacheBustedUrl);
return;
}
canvas.clear();
// Scale image to fit canvas with padding
const padding = 40;
const availableWidth = (canvas.width || 800) - padding;
const availableHeight = (canvas.height || 600) - padding;
const scale = Math.min(
availableWidth / img.width,
availableHeight / img.height
);
img.scale(scale);
img.set({
left: ((canvas.width || 800) - img.width * scale) / 2,
top: ((canvas.height || 600) - img.height * scale) / 2,
selectable: false,
evented: false,
hoverCursor: 'default',
});
imageRef.current = img;
canvas.add(img);
canvas.sendToBack(img);
centerImage(canvas, img, zoom / 100);
canvas.renderAll();
}, { crossOrigin: 'anonymous' });
}, [imageUrl]);
// Handle tool/mode changes
useEffect(() => {
if (!fabricCanvasRef.current) return;
const canvas = fabricCanvasRef.current;
// Remove all event handlers
canvas.off('mouse:down');
canvas.off('mouse:move');
canvas.off('mouse:up');
canvas.off('object:modified');
canvas.off('object:moving');
canvas.off('object:scaling');
// Set up handlers based on selection mode or advanced tool mode
if (advancedToolMode === 'smart-select') {
setupSmartSelectMode(canvas);
} else if (advancedToolMode === 'color-select') {
setupColorSelectMode(canvas);
} else if (selectionMode === 'rectangle') {
setupRectangleMode(canvas);
} else if (selectionMode === 'ellipse') {
setupEllipseMode(canvas);
} else if (selectionMode === 'lasso') {
setupLassoMode(canvas);
} else if (selectionMode === 'move') {
setupMoveMode(canvas);
} else if (selectionMode === 'pan') {
setupPanMode(canvas);
}
}, [selectionMode, advancedToolMode, onAdvancedToolClick, isProcessing]);
const setupMoveMode = (canvas) => {
// In move mode, allow selecting and moving selection objects
const sel = currentSelectionRef.current;
if (sel) {
sel.set({ selectable: true, evented: true });
canvas.setActiveObject(sel);
}
canvas.on('object:modified', (e) => {
if (e.target && e.target === currentSelectionRef.current) {
updateTransformedSelection(e.target);
}
});
};
const setupPanMode = (canvas) => {
let isPanning = false;
let lastPosX, lastPosY;
canvas.on('mouse:down', (e) => {
isPanning = true;
lastPosX = e.e.clientX;
lastPosY = e.e.clientY;
canvas.setCursor('grabbing');
});
canvas.on('mouse:move', (e) => {
if (!isPanning) return;
const deltaX = e.e.clientX - lastPosX;
const deltaY = e.e.clientY - lastPosY;
canvas.relativePan({ x: deltaX, y: deltaY });
lastPosX = e.e.clientX;
lastPosY = e.e.clientY;
});
canvas.on('mouse:up', () => {
isPanning = false;
canvas.setCursor('grab');
});
canvas.setCursor('grab');
};
const setupSmartSelectMode = (canvas) => {
canvas.on('mouse:down', (e) => {
if (isProcessing) return;
const pointer = canvas.getPointer(e.e);
const img = imageRef.current;
if (!img) return;
// Convert to image coordinates
const imgScale = img.scaleX;
const imgLeft = img.left;
const imgTop = img.top;
const x = Math.round((pointer.x - imgLeft) / imgScale);
const y = Math.round((pointer.y - imgTop) / imgScale);
// Check if click is within image bounds
if (x >= 0 && x < img.width && y >= 0 && y < img.height) {
onAdvancedToolClick?.(x, y, null);
}
});
canvas.setCursor('crosshair');
};
const setupColorSelectMode = (canvas) => {
canvas.on('mouse:down', (e) => {
if (isProcessing) return;
const pointer = canvas.getPointer(e.e);
const img = imageRef.current;
if (!img) return;
// Convert to image coordinates
const imgScale = img.scaleX;
const imgLeft = img.left;
const imgTop = img.top;
const x = Math.round((pointer.x - imgLeft) / imgScale);
const y = Math.round((pointer.y - imgTop) / imgScale);
// Check if click is within image bounds
if (x >= 0 && x < img.width && y >= 0 && y < img.height) {
// Get pixel color from canvas
const ctx = canvas.getContext('2d');
if (ctx) {
// Calculate actual canvas position accounting for viewport transform
const vpt = canvas.viewportTransform;
const canvasX = pointer.x * vpt[0] + vpt[4];
const canvasY = pointer.y * vpt[3] + vpt[5];
const pixelData = ctx.getImageData(canvasX, canvasY, 1, 1).data;
const color = {
r: pixelData[0],
g: pixelData[1],
b: pixelData[2]
};
onAdvancedToolClick?.(x, y, color);
}
}
});
canvas.setCursor('crosshair');
};
const setupRectangleMode = (canvas) => {
let rect = null;
let isDown = false;
let startX, startY;
canvas.on('mouse:down', (e) => {
// Check if clicking on existing selection
const sel = currentSelectionRef.current;
if (e.target && e.target === sel) {
// Allow moving/transforming
return;
}
// Clear previous selection
if (sel) {
canvas.remove(sel);
setCurrentSelection(null);
}
isDown = true;
isDrawingRef.current = true;
const pointer = canvas.getPointer(e.e);
startX = pointer.x;
startY = pointer.y;
rect = new fabric.Rect({
left: startX,
top: startY,
width: 0,
height: 0,
fill: 'rgba(0, 136, 255, 0.2)',
stroke: '#0088ff',
strokeWidth: 2,
strokeDashArray: [5, 5],
selectable: true,
hasControls: true,
hasBorders: true,
cornerColor: '#0088ff',
cornerSize: 8,
transparentCorners: false,
borderColor: '#0088ff',
});
canvas.add(rect);
});
canvas.on('mouse:move', (e) => {
if (!isDown || !rect) return;
const pointer = canvas.getPointer(e.e);
const width = pointer.x - startX;
const height = pointer.y - startY;
rect.set({
width: Math.abs(width),
height: Math.abs(height),
left: width < 0 ? pointer.x : startX,
top: height < 0 ? pointer.y : startY,
});
canvas.renderAll();
});
canvas.on('mouse:up', () => {
if (isDown && rect && rect.width > 5 && rect.height > 5) {
isDown = false;
isDrawingRef.current = false;
setCurrentSelection(rect);
canvas.setActiveObject(rect);
updateSelection(rect, 'rectangle');
} else if (isDown && rect) {
// Selection too small, remove it
canvas.remove(rect);
isDown = false;
isDrawingRef.current = false;
}
});
canvas.on('object:modified', (e) => {
if (e.target === currentSelectionRef.current) {
updateTransformedSelection(e.target);
}
});
};
const setupEllipseMode = (canvas) => {
let ellipse = null;
let isDown = false;
let startX, startY;
canvas.on('mouse:down', (e) => {
const sel = currentSelectionRef.current;
if (e.target && e.target === sel) {
return;
}
if (sel) {
canvas.remove(sel);
setCurrentSelection(null);
}
isDown = true;
const pointer = canvas.getPointer(e.e);
startX = pointer.x;
startY = pointer.y;
ellipse = new fabric.Ellipse({
left: startX,
top: startY,
rx: 0,
ry: 0,
fill: 'rgba(0, 136, 255, 0.2)',
stroke: '#0088ff',
strokeWidth: 2,
strokeDashArray: [5, 5],
selectable: true,
hasControls: true,
hasBorders: true,
cornerColor: '#0088ff',
cornerSize: 8,
transparentCorners: false,
borderColor: '#0088ff',
});
canvas.add(ellipse);
});
canvas.on('mouse:move', (e) => {
if (!isDown || !ellipse) return;
const pointer = canvas.getPointer(e.e);
const rx = Math.abs(pointer.x - startX) / 2;
const ry = Math.abs(pointer.y - startY) / 2;
ellipse.set({
rx: rx,
ry: ry,
left: Math.min(startX, pointer.x),
top: Math.min(startY, pointer.y),
});
canvas.renderAll();
});
canvas.on('mouse:up', () => {
if (isDown && ellipse && ellipse.rx > 5 && ellipse.ry > 5) {
isDown = false;
setCurrentSelection(ellipse);
canvas.setActiveObject(ellipse);
updateSelection(ellipse, 'ellipse');
} else if (isDown && ellipse) {
canvas.remove(ellipse);
isDown = false;
}
});
canvas.on('object:modified', (e) => {
if (e.target === currentSelectionRef.current) {
updateTransformedSelection(e.target);
}
});
};
const setupLassoMode = (canvas) => {
let points = [];
let drawingLine = null;
let polygon = null;
canvas.on('mouse:down', (e) => {
const sel = currentSelectionRef.current;
if (e.target && e.target === sel) {
return;
}
if (sel) {
canvas.remove(sel);
setCurrentSelection(null);
}
isDrawingRef.current = true;
const pointer = canvas.getPointer(e.e);
points = [{ x: pointer.x, y: pointer.y }];
drawingLine = new fabric.Polyline(points, {
fill: 'transparent',
stroke: '#0088ff',
strokeWidth: 2,
selectable: false,
evented: false,
});
canvas.add(drawingLine);
});
canvas.on('mouse:move', (e) => {
if (!isDrawingRef.current) return;
const pointer = canvas.getPointer(e.e);
points.push({ x: pointer.x, y: pointer.y });
canvas.remove(drawingLine);
drawingLine = new fabric.Polyline([...points], {
fill: 'transparent',
stroke: '#0088ff',
strokeWidth: 2,
selectable: false,
evented: false,
});
canvas.add(drawingLine);
canvas.renderAll();
});
canvas.on('mouse:up', () => {
if (isDrawingRef.current && points.length > 5) {
isDrawingRef.current = false;
lassoPoints.current = [...points];
canvas.remove(drawingLine);
polygon = new fabric.Polygon(points, {
fill: 'rgba(0, 136, 255, 0.2)',
stroke: '#0088ff',
strokeWidth: 2,
strokeDashArray: [5, 5],
selectable: true,
hasControls: true,
hasBorders: true,
cornerColor: '#0088ff',
cornerSize: 8,
transparentCorners: false,
borderColor: '#0088ff',
});
canvas.add(polygon);
canvas.setActiveObject(polygon);
setCurrentSelection(polygon);
updateSelection(polygon, 'lasso');
} else if (isDrawingRef.current) {
isDrawingRef.current = false;
canvas.remove(drawingLine);
}
});
canvas.on('object:modified', (e) => {
if (e.target === currentSelectionRef.current) {
updateTransformedSelection(e.target);
}
});
};
const updateSelection = (selection, type) => {
if (!selection || !imageRef.current) return;
const img = imageRef.current;
const imgScale = img.scaleX;
const imgLeft = img.left;
const imgTop = img.top;
let bbox, selectionData = null;
if (type === 'rectangle') {
bbox = {
x: Math.round((selection.left - imgLeft) / imgScale),
y: Math.round((selection.top - imgTop) / imgScale),
width: Math.round(selection.width / imgScale),
height: Math.round(selection.height / imgScale),
};
} else if (type === 'ellipse') {
bbox = {
x: Math.round((selection.left - imgLeft) / imgScale),
y: Math.round((selection.top - imgTop) / imgScale),
width: Math.round((selection.rx * 2) / imgScale),
height: Math.round((selection.ry * 2) / imgScale),
};
} else if (type === 'lasso') {
const bounds = selection.getBoundingRect();
bbox = {
x: Math.round((bounds.left - imgLeft) / imgScale),
y: Math.round((bounds.top - imgTop) / imgScale),
width: Math.round(bounds.width / imgScale),
height: Math.round(bounds.height / imgScale),
};
const relativePoints = lassoPoints.current.map(p => [
Math.round((p.x - imgLeft) / imgScale) - bbox.x,
Math.round((p.y - imgTop) / imgScale) - bbox.y,
]);
selectionData = { points: relativePoints };
}
onSelectionChange?.({
type,
bbox,
selectionData,
});
};
const updateTransformedSelection = (selection) => {
if (!selection || !imageRef.current) return;
const img = imageRef.current;
const imgScale = img.scaleX;
const imgLeft = img.left;
const imgTop = img.top;
const bounds = selection.getBoundingRect(true);
const bbox = {
x: Math.round((bounds.left - imgLeft) / imgScale),
y: Math.round((bounds.top - imgTop) / imgScale),
width: Math.round(bounds.width / imgScale),
height: Math.round(bounds.height / imgScale),
};
let selectionData = null;
const type = selection.type === 'polygon' ? 'lasso' : (selection.type === 'ellipse' ? 'ellipse' : 'rectangle');
if (type === 'lasso' && lassoPoints.current.length > 0) {
const matrix = selection.calcTransformMatrix();
const transformedPoints = lassoPoints.current.map(p => {
const transformed = fabric.util.transformPoint(
new fabric.Point(p.x, p.y),
matrix
);
return [
Math.round((transformed.x - imgLeft) / imgScale) - bbox.x,
Math.round((transformed.y - imgTop) / imgScale) - bbox.y,
];
});
selectionData = { points: transformedPoints };
}
onSelectionChange?.({
type,
bbox,
selectionData,
});
};
const clearSelection = () => {
const canvas = fabricCanvasRef.current;
const sel = currentSelectionRef.current;
if (sel && canvas) {
canvas.remove(sel);
setCurrentSelection(null);
onSelectionChange?.(null);
}
};
const handleZoomIn = () => {
if (!fabricCanvasRef.current) return;
const canvas = fabricCanvasRef.current;
let newZoom = canvas.getZoom() * 1.2;
if (newZoom > 10) newZoom = 10;
canvas.setZoom(newZoom);
setCurrentZoom(newZoom);
if (onZoomChangeRef.current) {
onZoomChangeRef.current(newZoom);
}
};
const handleZoomOut = () => {
if (!fabricCanvasRef.current) return;
const canvas = fabricCanvasRef.current;
let newZoom = canvas.getZoom() / 1.2;
if (newZoom < 0.1) newZoom = 0.1;
canvas.setZoom(newZoom);
setCurrentZoom(newZoom);
if (onZoomChangeRef.current) {
onZoomChangeRef.current(newZoom);
}
};
const handleZoomReset = () => {
if (!fabricCanvasRef.current) return;
const canvas = fabricCanvasRef.current;
canvas.setZoom(1);
canvas.setViewportTransform([1, 0, 0, 1, 0, 0]);
setCurrentZoom(1);
if (onZoomChangeRef.current) {
onZoomChangeRef.current(1);
}
};
return (
<div className="canvas-container">
<canvas ref={canvasRef} />
<div className="canvas-controls">
<div className="zoom-controls">
<button onClick={handleZoomOut} title="Zoom Out"></button>
<span className="zoom-level">{Math.round(currentZoom * 100)}%</span>
<button onClick={handleZoomIn} title="Zoom In">+</button>
<button onClick={handleZoomReset} title="Reset Zoom"></button>
</div>
{currentSelection && (
<button className="clear-selection-btn" onClick={clearSelection}>
Clear Selection
</button>
)}
</div>
{(advancedToolMode === 'smart-select' || advancedToolMode === 'color-select') && (
<div className="tool-mode-indicator">
{advancedToolMode === 'smart-select' ? 'Click on an object to select it' : 'Click on a color to select similar pixels'}
</div>
)}
</div>
);
});
ImageCanvas.displayName = 'ImageCanvas';
export default ImageCanvas;
+732
View File
@@ -0,0 +1,732 @@
/*****************\
| UI Button Group |
\*****************/
.ui_button_group {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.ui_button_group.no_wrap {
flex-wrap: nowrap;
}
.ui_button_group.stacked {
margin: .75rem 0;
}
.ui_button_group.stacked:first-child {
margin-top: 0;
}
.ui_button_group.stacked:last-child {
margin-bottom: 0;
}
.ui_button_group > button,
.ui_button_group > input[type="button"] {
border-radius: 0;
}
.ui_button_group > button:focus,
.ui_button_group > input[type="button"]:focus {
z-index: 1;
}
.ui_button_group > button + button,
.ui_button_group > button + input[type="button"],
.ui_button_group > input[type="button"] + button,
.ui_button_group > input[type="button"] + input[type="button"] {
margin-left: -1px;
}
.ui_button_group > button:first-child,
.ui_button_group > input[type="button"]:first-child {
border-radius: var(--button-border-radius) 0 0 var(--button-border-radius);
}
.ui_button_group > button:last-child,
.ui_button_group > input[type="button"]:last-child {
border-radius: 0 var(--button-border-radius) var(--button-border-radius) 0;
}
/****************\
| UI Color Input |
\****************/
.ui_color_input {
display: inline-block;
padding: 0;
margin: 0;
position: relative;
overflow: hidden;
vertical-align: middle;
}
.ui_color_input input[type="color"] {
display: block;
cursor: pointer;
padding: 0;
border: .2rem solid var(--input-background-color);
width: 3rem;
}
.ui_color_input .alpha_overlay {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAABhWlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9bRdGKiBlERDJUJwuiIo5ahSJUCLVCqw4mL/2DJg1Jiouj4Fpw8Gex6uDirKuDqyAI/oA4OTopukiJ9yWFFjFeeLyP8+45vHcfEKyVmGa1jQOabpvJeExMZ1bFjld0YxgBCOiTmWXMSVICvvV1T51Ud1Ge5d/3Z/WoWYsBAZF4lhmmTbxBPL1pG5z3iQVWkFXic+Ixky5I/Mh1xeM3znmXgzxTMFPJeWKBWMy3sNLCrGBqxFPEEVXTKT+Y9ljlvMVZK1VY4578heGsvrLMdVpDiGMRS5AgQkEFRZRgI0q7ToqFJJ3HfPyDrl8il0KuIhg5FlCGBtn1g//B79lauckJLykcA9pfHOdjBOjYBepVx/k+dpz6CRB6Bq70pr9cA2Y+Sa82tcgR0LsNXFw3NWUPuNwBBp4M2ZRdKUQrmMsB72f0TRmg/xboWvPm1jjH6QOQolklboCDQ2A0T9nrPu/ubJ3bvz2N+f0AL+pyjMZuudYAAAAJcEhZcwAALiMAAC4jAXilP3YAAAAHdElNRQfkCx4BHwaj7CMVAAAAGXRFWHRDb21tZW50AENyZWF0ZWQgd2l0aCBHSU1QV4EOFwAAAC5JREFUOMtjfPfuHQNuICgoiEeWiYECMKp5ZGhm/P//Px7p9+/fjwbYqGZKNAMAANAI7r7rfkQAAAAASUVORK5CYII=');
background-size: 100% 100%;
position: absolute;
top: 3px;
left: 3px;
right: 3px;
bottom: 3px;
pointer-events: none;
}
/**************************\
| UI Color Picker Gradient |
\**************************/
.ui_color_picker_gradient {
padding: 0 0 80% 0;
position: relative;
width: 100%;
}
.ui_color_picker_gradient .primary_pick {
position: absolute;
left: 86%;
right: 0;
top: 0;
bottom: 0;
background: white;
}
.ui_color_picker_gradient .secondary_pick {
position: absolute;
left: 0;
right: 17%;
top: 0;
bottom: 0;
border: 1px solid var(--border-color);
background: green;
}
.ui_color_picker_gradient .secondary_pick:focus {
outline: 0;
border: 1px solid var(--input-border-color-active);
}
.ui_color_picker_gradient .secondary_pick .saturation_gradient {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
}
.ui_color_picker_gradient .secondary_pick .value_gradient {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: linear-gradient(to top, #000, rgba(204, 154, 129, 0));
}
.ui_color_picker_gradient .secondary_pick .handle {
position: absolute;
left: 0;
right: auto;
top: 0;
bottom: auto;
pointer-events: none;
}
.ui_color_picker_gradient .secondary_pick .handle:before {
content: '';
display: block;
position: absolute;
left: -.6rem;
top: -.6rem;
height: .3rem;
width: .3rem;
border: .4rem solid #999;
border-radius: 1000px;
}
.ui_color_picker_gradient .secondary_pick .handle:after {
content: '';
display: block;
position: absolute;
left: -.5rem;
top: -.5rem;
height: .5rem;
width: .5rem;
border: .2rem solid white;
border-radius: 1000px;
}
.ui_color_picker_gradient .primary_pick .ui_range {
border-color: rgba(1, 1, 1, 0.1);
}
.ui_color_picker_gradient .primary_pick .ui_range:focus {
border-color: var(--input-border-color-active);
}
/*****************\
| UI Color Sample |
\*****************/
.ui_color_sample {
border: 1px solid #999;
box-shadow: 0 0 0 1px #555 inset;
display: block;
height: 28px;
width: 28px;
}
/***************\
| UI Flex Group |
\***************/
.ui_flex_group {
display: flex;
flex-direction: row;
}
.ui_flex_group.stacked {
margin: .75rem 0;
}
.ui_flex_group.stacked:first-child {
margin-top: 0;
}
.ui_flex_group.stacked:last-child {
margin-bottom: 0;
}
.ui_flex_group.column {
flex-direction: column;
}
.ui_flex_group.justify_content_center {
justify-content: center;
}
.ui_flex_group.justify_content_start {
justify-content: flex-start;
}
.ui_flex_group.justify_content_end {
justify-content: flex-end;
}
.ui_flex_group.justify_content_space_around {
justify-content: space-around;
}
.ui_flex_group.justify_content_space_between {
justify-content: space-between;
}
.ui_flex_group.align_items_baseline {
align-items: baseline;
}
.ui_flex_group.align_items_center {
align-items: center;
}
.ui_flex_group.align_items_start {
align-items: flex-start;
}
.ui_flex_group.align_items_end {
align-items: flex-end;
}
.ui_flex_group.align_items_stretch {
align-items: stretch;
}
/****************\
| UI Icon Button |
\****************/
.ui_icon_button {
height: 2.8rem;
line-height: 2.8rem;
}
.ui_icon_button.input_height {
height: 2.4rem;
line-height: 2.4rem;
}
.ui_icon_button > svg {
display: block;
font-size: 1.6rem;
}
.ui_icon_button > img {
display: block;
margin: 0 auto;
}
button img{
filter: var(--menu-icons-filter);
}
/****************\
| UI Input Group |
\****************/
.ui_input_group {
display: flex;
flex-direction: row;
min-height: 2.4rem;
width: 100%;
}
.ui_input_group.stacked {
margin: .75rem 0;
}
.ui_input_group.stacked:first-child {
margin-top: 0;
}
.ui_input_group.stacked:last-child {
margin-bottom: 0;
}
.ui_input_group > input,
.ui_input_group > .ui_number_input,
.ui_input_group > .ui_range,
.ui_input_group > .ui_color_sample {
border-radius: 0;
height: auto;
min-width: 0;
}
.ui_input_group > .ui_color_sample {
border: none;
width: 100%;
}
.ui_input_group > :first-child {
border-radius: var(--input-border-radius) 0 0 var(--input-border-radius);
}
.ui_input_group > :last-child {
border-radius: 0 var(--input-border-radius) var(--input-border-radius) 0;
}
.ui_input_group > label {
display: flex;
align-items: center;
border: 1px solid var(--input-group-border-color);
border-right: 0;
margin: 0;
padding: 0 .75rem;
}
.ui_input_group > .ui_range + input,
.ui_input_group > .ui_range + .ui_number_input {
margin-left: -1px;
}
.ui_input_grid {
border-radius: var(--input-border-radius);
box-shadow: 0 1px 0 0 rgba(1, 1, 1, 0.1);
}
.ui_input_grid.stacked {
margin: .75rem 0;
}
.ui_input_grid.stacked:first-child {
margin-top: 0;
}
.ui_input_grid.stacked:last-child {
margin-bottom: 0;
}
:not(.ui_input_grid) > .ui_input_group {
border-radius: var(--input-border-radius);
box-shadow: 0 1px 0 0 rgba(1, 1, 1, 0.1);
}
.ui_input_grid > .ui_input_group {
margin: -1px 0;
}
.ui_input_grid > .ui_input_group > :first-child,
.ui_input_grid > .ui_input_group > :last-child {
border-radius: 0;
}
.ui_input_grid > .ui_input_group:first-child {
margin-top: 0;
}
.ui_input_grid > .ui_input_group:first-child > :first-child {
border-radius: var(--input-border-radius) 0 0 0;
}
.ui_input_grid > .ui_input_group:first-child > :last-child {
border-radius: 0 var(--input-border-radius) 0 0;
}
.ui_input_grid > .ui_input_group:last-child {
margin-bottom: 0;
}
.ui_input_grid > .ui_input_group:last-child > :first-child {
border-radius: 0 0 0 var(--input-border-radius);
}
.ui_input_grid > .ui_input_group:last-child > :last-child {
border-radius: 0 0 var(--input-border-radius) 0;
}
/*****************\
| UI Number Input |
\*****************/
.ui_number_input {
border: 1px solid var(--input-border-color);
border-radius: var(--input-border-radius);
display: inline-block;
padding: 0;
margin: 0;
position: relative;
overflow: hidden;
vertical-align: middle;
}
.ui_number_input > input[type="number"]::-webkit-outer-spin-button,
.ui_number_input > input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
.ui_number_input > input[type="number"] {
border: none;
border-radius: 0;
-moz-appearance: textfield;
appearance: textfield;
padding-right: 2.5rem;
padding-right: calc(var(--number-input-arrow-width) + .5rem);
width: 100%;
}
.ui_number_input > .increase_number,
.ui_number_input > .decrease_number {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 2rem;
width: var(--number-input-arrow-width);
border-radius: 0;
border: 1px solid var(--input-border-color);
border-right: none;
padding: 0;
margin: 0;
}
.ui_number_input > ::-moz-focus-inner {
border: 0;
}
.ui_number_input > .increase_number:focus,
.ui_number_input > .decrease_number:focus {
outline: 0;
}
.ui_number_input > .increase_number {
right: 0;
top: 0;
bottom: 50%;
border-top: none;
}
.ui_number_input > .increase_number::after {
content: '';
display: block;
width: 0;
height: 0;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-bottom: 3px solid var(--input-text-color);
}
.ui_number_input > .decrease_number {
right: 0;
top: calc(50% - 1px);
bottom: 0;
border-bottom: none;
}
.ui_number_input > .decrease_number::after {
content: '';
display: block;
width: 0;
height: 0;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 3px solid var(--input-text-color);
}
/**********\
| UI Range |
\**********/
:root {
--range-handle-width: 18px;
}
.ui_range {
display: flex;
flex-direction: row;
background: var(--input-background-color);
border: 1px solid var(--input-border-color);
border-radius: 1000px;
height: 1.8rem;
overflow: visible;
outline: 0;
padding: 0 calc(var(--range-handle-width) / 2);
position: relative;
width: 100%;
}
.ui_range:focus {
border-color: var(--input-border-color-active);
z-index: 1;
}
.ui_range.active {
cursor: col-resize;
}
.ui_range .padded_track {
position: absolute;
left: calc(var(--range-handle-width) / 2);
right: calc(var(--range-handle-width) / 2);
top: 0;
bottom: 0;
}
.ui_range .bar {
overflow: visible;
position: relative;
width: 0%;
}
.ui_range .handle {
background: var(--input-text-color);
border: 1px solid var(--border-color);
border-radius: 1000px;
box-sizing: border-box;
cursor: col-resize;
display: block;
height: 1.8rem;
width: var(--range-handle-width);
position: absolute;
top: 50%;
right: 0;
transform: translate(50%, -50%);
}
.ui_range.color_picker .handle {
background: none;
border: none;
border-radius: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
height: auto;
top: 0;
bottom: 0;
transform: translateX(50%);
}
.ui_range.color_picker .handle::before {
content: '';
display: block;
width: 0;
height: 0;
border-left: .5rem solid transparent;
border-right: .5rem solid transparent;
border-top: .7rem solid white;
}
.ui_range.color_picker .handle::after {
content: '';
display: block;
width: 0;
height: 0;
border-left: .5rem solid transparent;
border-right: .5rem solid transparent;
border-bottom: .7rem solid black;
}
.ui_range.color_picker .handle:hover::before {
border-top-color: #eaeaea;
}
.ui_range.color_picker .handle:hover::after {
border-bottom-color: #222;
}
.ui_range.vertical {
flex-direction: column;
justify-content: flex-end;
height: 100%;
width: 1.8rem;
padding: calc(var(--range-handle-width) / 2) 0;
}
.ui_range.vertical.active {
cursor: row-resize;
}
.ui_range.vertical .padded_track {
left: 0;
right: 0;
top: calc(var(--range-handle-width) / 2);
bottom: calc(var(--range-handle-width) / 2);
}
.ui_range.vertical .bar {
width: 100%;
height: 0%;
}
.ui_range.vertical .handle {
transform: translate(50%, -50%);
top: 0;
right: 50%;
cursor: row-resize;
}
.ui_range.vertical.color_picker_thin {
padding: 1px 0;
border-radius: 0;
width: 100%;
}
.ui_range.vertical.color_picker_thin .padded_track {
top: 0;
bottom: 0;
}
.ui_range.vertical.color_picker_thin .handle {
border-radius: 0;
width: 100%;
height: .5rem;
}
/*************\
| UI Swatches |
\*************/
.ui_swatches {
display: flex;
justify-content: center;
}
.ui_swatches .swatch_group {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: auto;
border-radius: var(--input-border-radius);
border: 1px solid var(--border-color);
border-right: transparent;
box-shadow: 0 1px 0 0 rgba(1, 1, 1, 0.1);
overflow: hidden;
max-height: calc(2.3rem);
}
.ui_swatches .swatch_group:focus {
outline: 0;
box-shadow: 0 0 0 1px var(--input-border-color-active);
}
.ui_swatches .swatch_group.rows_2 {
max-height: calc(4.6rem - 1px);
}
.ui_swatches .swatch_group.rows_3 {
max-height: calc(6.9rem - 2px);
}
.ui_swatches .swatch_group.cols_1 .swatch {
width: 100%;
}
.ui_swatches .swatch_group.cols_2 .swatch {
width: 50%;
}
.ui_swatches .swatch_group.cols_3 .swatch {
width: 33.33%;
}
.ui_swatches .swatch_group.cols_4 .swatch {
width: 25%;
}
.ui_swatches .swatch_group.cols_5 .swatch {
width: 20%;
}
.ui_swatches .swatch_group.cols_6 .swatch {
width: 16.66%;
}
.ui_swatches .swatch_group.cols_7 .swatch {
width: 14.29%;
}
.ui_swatches .swatch_group.cols_8 .swatch {
width: 12.5%;
}
.ui_swatches .swatch {
background: white;
display: inline-block;
position: relative;
border: 1px solid var(--border-color);
border-radius: 0;
box-shadow: 0 0 0 1px white inset;
margin: -1px 0 0 -1px;
padding: 0;
height: 2.3rem;
min-width: 2.3rem;
flex-grow: 1;
}
.ui_swatches .swatch:hover,
.ui_swatches .swatch:focus {
background: white;
box-shadow: 0 0 0 2px white inset, 0 0 0 3px var(--border-color) inset;
}
.ui_swatches .swatch:hover:after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.2) 50%, rgba(0, 0, 0, 0.1) 51%, rgba(0, 0, 0, 0.1) 100%);
}
.ui_swatches .swatch.active {
box-shadow: 0 0 0 3px var(--button-text-color-active) inset, 0 0 0 4px var(--border-color) inset;
}
/******************\
| UI Toggle Button |
\******************/
.ui_toggle_button {
padding-left: 2.6rem !important;
position: relative;
}
.ui_toggle_button:before {
background-color: var(--button-toggle-background-color);
background-image: url('data:image/svg+xml;utf8,<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-x" fill="white" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"/></svg>');
background-position: center;
background-repeat: no-repeat;
border-radius: var(--button-border-radius) 0 0 var(--button-border-radius);
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 1.8rem;
content: '';
}
.ui_toggle_button[aria-pressed="true"]:before {
background-color: var(--button-text-color-active);
background-image: url('data:image/svg+xml;utf8,<svg width="0.7em" height="1em" viewBox="0 0 16 16" class="bi bi-check2" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg>');
}
/* media */
.media-paging{
width: 100%;
margin: 10px 0;
text-align: center;
}
.media-paging button{
background-color: var(--button-background-color);
color: var(--text-color);
}
.media-paging button.selected{
background-color: var(--background-color-active);
color: var(--text-color-active);
}
/* global search */
#global_search_results{
padding-top: 10px;
font-size: 14px;
}
#global_search_results .search-result {
padding: 3px 5px;
}
#global_search_results .search-result.active{
background-color: var(--background-color-active);
color: var(--text-color-active);
border-radius: 2px;
}
#global_search_results b{
color: var(--text-color-red);
}
.popup.shortcuts table{
line-height: 1;
}
+971
View File
@@ -0,0 +1,971 @@
.wrapper{
display: -ms-grid;
display: grid;
margin: 0;
position: fixed; /* dont change it, vh does not work on mobiles with bottom footer */
top: 30px;
right: 0;
left: 0;
bottom: 5px;
height: auto;
overflow: hidden;
-ms-grid-rows: auto 1fr;
grid-template-rows: auto 1fr;
-ms-grid-columns: auto 1fr auto;
grid-template-columns: auto 1fr auto;
grid-template-areas:
"submenu submenu submenu"
"sidebar_left main sidebar_right";
}
.trn{}
.toggle{
cursor: pointer;
}
.hidden{
display:none;
}
.center{
text-align: center;
}
.pointer{
cursor: pointer;
}
.clear{
clear:both;
}
.displayBlock{
display: block;
}
.bold{
font-weight: bold;
}
.left{
float: left;
}
.right{
float: right;
}
.grey{
color:grey;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version */
}
.block{
position: relative;
background-color: rgba(255, 255, 255, 0.2);
background-color: var(--block-background-color);
border: 1px solid rgba(0, 0, 0, 0.5);
border: 1px solid var(--border-color);
margin-bottom: 10px;
user-select: none;
border-radius: 4px;
}
.sidebar_right .block{
background-color: #68727b;
background-color: var(--block-background-color);
border-bottom: none;
box-shadow: 0 -2px 0 0 var(--header-background-color) inset;
}
.block:last-child{
margin-bottom: 0;
}
.block h2{
position: relative;
padding: 2px 5px 2px 6px;
margin: 0;
font-size: 110%;
background-color: rgba(255, 255, 255, 0.3);
background-color: var(--header-background-color);
border-bottom: #555;
border-radius: 4px 4px 0 0;
}
.block.toggled h2, .block h2.toggled:after{
border: none;
}
.block h2.toggle:before{
/* icon */
position:absolute;
content:'';
width: 0;
height: 0;
right: 10px;
top: 10px;
border-style: solid;
border-width: 0 4px 5px 4px;
border-color: transparent transparent var(--text-color-muted) transparent;
}
.block h2.toggled:before{
/* icon */
border-width: 5px 4px 0 4px;
border-color: var(--text-color-muted) transparent transparent transparent;
}
.block .content{
padding: 7.5px 5px;
}
.block_section {
margin: .75rem 0;
}
.block_section:first-child {
margin-top: 0;
}
.block_section:last-child {
margin-bottom: 0;
}
.error{
padding:20px;
margin:10px;
border:1px solid #ff0000;
background-color:#ffffff;
width:500px;
font-weight:bold;
}
/* color chooser */
body .sp-replacer{
width: 100%;
height: 40px;
}
body .sp-preview{
width: calc(100% - 20px);
height: 100%;
}
/* ========== header ======================================================== */
.logo{
position: relative;
display: inline-block;
height: 30px;
width: 110px;
padding: 5px 5px 5px 36px;
margin: 5px;
font-size: 14px;
text-decoration: none;
font-weight: bold;
color: #ffffff;
color: var(--text-color);
}
.logo:after{
position:absolute;
content:"";
left: 0;
top: 0;
width: 31px;
height: 30px;
background: url('images/logo.svg') no-repeat center center;
background-size: auto 28px;
filter: var(--mobile-menu-toggle-filter);
}
.logo:hover:after{
left: 2px;
}
.about-logo{
margin-left:22%;
}
.about-name{
font-size:15px;
font-weight:bold;
}
.undo_button {
display: none;
width: 50px;
height: 50px;
top: 0;
border: 0;
outline: none;
cursor: pointer;
filter: var(--mobile-menu-toggle-filter);
background: url(images/icons/undo.svg) no-repeat center center;
background-size: auto 25px;
margin-left: 10px;
}
.undo_button:hover {
background-color: transparent;
}
@media screen and (max-width: 700px){
.undo_button {
display: block;
}
}
/* ========== sub-header ==================================================== */
.submenu{
-ms-grid-row: 1;
-ms-grid-column: 1;
-ms-grid-column-span: 3;
grid-area: submenu;
display: flex;
flex-direction: row;
align-items: center;
background-color: rgba(255, 255, 255, 0.2);
background-color: var(--section-background-color);
overflow: hidden;
margin-bottom: 5px;
}
.attributes{
display: flex;
flex-wrap: nowrap;
background-color: var(--area-background-color);
width: calc(100% - 125px);
margin-top: 5px;
margin-bottom: 5px !important;
padding: 3px 10px 3px 10px;
border: 0;
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
min-height: 30px;
}
.attributes .item{
display: inline-flex;
align-items: center;
margin-right: 20px;
}
.attributes .item > label {
margin: 0 .5rem 0 0;
}
.attributes input[type="number"]{
width: 60px;
margin-right: 5px;
}
.attributes input[type="color"] {
cursor: pointer;
padding: 0;
border: .2rem solid var(--input-background-color);
width: 3rem;
}
.attributes .item > button:not(.ui_icon_button){
display: inline-block;
padding: 3px 10px;
}
/* ========== left sidebar ================================================== */
.sidebar_left{
-ms-grid-row: 2;
-ms-grid-column: 1;
grid-area: sidebar_left;
display: flex;
flex-direction: row;
flex-wrap: wrap;
background-color: var(--section-background-color);
padding: 0 5px 5px 0;
margin-right: 5px;
overflow: hidden;
align-self: start;
width: 40px;
overflow-y: auto;
max-height: 100%;
}
.sidebar_left .item{
position: relative;
display:block;
background-color: var(--area-background-color);
height: 25px;
width: 30px;
margin: 5px 0 0 5px;
overflow: hidden;
cursor: pointer;
}
.sidebar_left .item:after{
position: absolute;
content: '';
left:0;
top:0;
bottom:0;
right:0;
filter: var(--menu-icons-filter);
background-position: center center;
background-repeat: no-repeat;
background-size: 20px 20px;
}
.sidebar_left .item:hover{
background-color: var(--background-color-hover);
}
.sidebar_left .item.active{
background-color: var(--background-color-active);
color: var(--text-color-active);
}
.sidebar_left .item.active:after{
filter: var(--menu-icons-filter-active);
}
/*
IMPORTANT: any new icon should also must be added on /service-worker.js + its version should be updated - FEATURE DISABLED
*/
.sidebar_left .select:after{ background-image: url('images/icons/select.svg'); }
.sidebar_left .selection:after{ background-image: url('images/icons/selection.svg'); }
.sidebar_left .brush:after{ background-image: url('images/icons/brush.svg'); }
.sidebar_left .pencil:after{ background-image: url('images/icons/pencil.svg'); }
.sidebar_left .pick_color:after{ background-image: url('images/icons/pick_color.svg'); }
.sidebar_left .erase:after{ background-image: url('images/icons/erase.svg'); }
.sidebar_left .magic_erase:after{ background-image: url('images/icons/magic_erase.svg'); }
.sidebar_left .fill:after{ background-image: url('images/icons/fill.svg'); }
.sidebar_left .media:after{ background-image: url('images/icons/media.svg'); }
.sidebar_left .shape:after{ background-image: url('images/icons/shape.svg'); }
.sidebar_left .text:after{ background-image: url('images/icons/text.svg'); background-size: 16px auto; }
.sidebar_left .gradient:after{ background-image: url('images/icons/gradient.png'); background-size: 18px 12px; filter: none; }
.sidebar_left .clone:after{ background-image: url('images/icons/clone.svg'); }
.sidebar_left .crop:after{ background-image: url('images/icons/crop.svg'); }
.sidebar_left .blur:after{ background-image: url('images/icons/blur.svg'); }
.sidebar_left .sharpen:after{ background-image: url('images/icons/sharpen.svg'); }
.sidebar_left .desaturate:after{ background-image: url('images/icons/desaturate.svg'); }
.sidebar_left .bulge_pinch:after{ background-image: url('images/icons/bulge_pinch.svg'); }
.sidebar_left .animation:after{ background-image: url('images/icons/animation.svg'); }
.sidebar_left .smart_select:after{ background-image: url('images/icons/smart_select.svg'); }
.sidebar_left .brush_select:after{ background-image: url('images/icons/brush_select.svg'); }
.sidebar_left .ai_inpaint:after{ background-image: url('images/icons/ai_inpaint.svg'); }
.sidebar_left .ai_edit:after{ background-image: url('images/icons/ai_edit.svg'); }
.sidebar_left .magic_wand:after{ background-image: url('images/icons/magic_wand.svg'); }
.sidebar_left .lasso:after{ background-image: url('images/icons/lasso.svg'); }
.sidebar_left .ellipse_select:after{ background-image: url('images/icons/ellipse_select.svg'); }
@media screen and (max-width:550px){
#sidebar_left{
left: -110px;
}
}
/* ========== right sidebar ================================================= */
.sidebar_right{
-ms-grid-row: 2;
-ms-grid-column: 3;
grid-area: sidebar_right;
z-index: 2;
display: flex;
flex-direction: column;
transition: 0.2s;
overflow-x: hidden;
overflow-y: scroll;
margin: 0 5px;
width: 200px;
}
.sidebar_right.active{
right: 0 !important;
}
.sidebar_right .block.layers{
flex: 1;
}
.sidebar_right .block.layers .content{
padding-bottom: 25px;
}
/* preview */
.canvas_preview_wrapper{
position:relative;
height:100px;
margin: 5px 5px 10px 5px;
}
.canvas_preview_details{
padding: 0 5px;
}
.canvas_preview_details button{
margin: 0;
}
.preview canvas{
cursor: pointer;
}
.details input{
padding: 5px 10px;
}
/* color */
.color_area{
border: 1px solid #444;
width: calc(100% - 10px);
height: 40px;
cursor: pointer;
margin: 5px;
}
/* layers */
.layers_list{
margin-top: 10px;
}
.layers_arrow{
display:inline-block;
float:right;
margin-left:5px;
padding:1px 8px;
border:1px solid #444;
border-color: var(--border-color);
text-decoration:none;
color:var(--text-color);
font-size:12px;
}
.layer_add{
display:inline-block;
padding:1px 8px;
margin-right: 10px;
background-color: #419147;
background-color: var(--background-color-active);
border:1px solid #444;
border-color: var(--border-color);
color: var(--text-color-active);
cursor:pointer;
text-decoration:none;
}
.layers_list .item{
margin-bottom:2px;
}
.layers_list .layer_name{
display:block;
padding:1px 5px 3px 5px;
height:19px;
width: calc(100% - 44px);
text-align: left;
overflow:hidden;
background-color:#989898;
background-color: var(--area-background-color);
border:1px solid #393939;
border-color: var(--border-color);
border-radius:3px;
cursor:pointer;
overflow:hidden;
font-size: 12px;
color:var(--text-color);
white-space: nowrap;
}
.layers_list .item.shorter .layer_name{
width: calc(100% - 63px);
}
.layers_list .item.active .layer_name{
background-color: var(--background-color-active);
color: var(--text-color-active);
}
.layers_list .arrow_down{
position: relative;
float:left;
margin-right: 5px;
width:10px;
height:19px;
opacity: 0.4;
}
.layers_list .arrow_down:after{
position: absolute;
content: '';
left:0;
top:0;
bottom:0;
right:0;
filter: var(--menu-icons-filter);
background: url('images/icons/arrow-down.svg') no-repeat center center;
background-size: 12px auto;
}
.layers_list .visibility{
position: relative;
float:left;
cursor:pointer;
padding:0px 3px 0px 3px;
margin-right: 5px;
width:20px;
height:19px;
opacity:0.1;
border: none;
background: transparent;
box-shadow: none;
}
.layers_list .visibility:after{
position: absolute;
content: '';
left:0;
top:0;
bottom:0;
right:0;
filter: var(--menu-icons-filter);
background: url('images/icons/view.svg') no-repeat center center;
background-size: 18px auto;
}
.layers_list .visible{ opacity:0.4; }
.layers_list .delete{
float:right;
cursor:pointer;
padding:0px 3px 0px 3px;
width:12px;
height:19px;
margin-left: 5px;
background: transparent url(images/icons/delete.svg) no-repeat center center;
background-size: 10px 10px;
border: none;
box-shadow: none;
}
/* filters */
.layers_list .filters{
margin-bottom: 5px;
}
.layers_list .filter{
margin-bottom: 2px;
margin-left: 30px;
opacity: 0.7;
}
.layers_list .filter .layer_name{
position: relative;
}
.layers_list .filter .layer_name:after{
position:absolute;
content:"fx";
right: -4px;
top:1px;
bottom:0;
width: 20px;
}
/* Layer context menu */
.layer_context_menu{
display: none;
position: fixed;
z-index: 1000;
background: var(--section-background-color);
border: 1px solid var(--border-color);
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
min-width: 150px;
}
.layer_context_menu ul{
list-style: none;
margin: 0;
padding: 5px 0;
}
.layer_context_menu li{
padding: 6px 15px;
cursor: pointer;
color: var(--text-color);
font-size: 13px;
}
.layer_context_menu li:hover{
background: var(--background-color-active);
color: var(--text-color-active);
}
.layer_context_menu li.separator{
height: 1px;
background: var(--border-color);
margin: 5px 10px;
padding: 0;
cursor: default;
}
.layer_context_menu li.separator:hover{
background: var(--border-color);
}
.layer_scale{
display:inline-block;
padding:1px 8px;
margin-right: 5px;
border:1px solid #444;
border-color: var(--border-color);
color: var(--text-color);
cursor:pointer;
}
/* My Library browser */
.library-browser{
max-height: 400px;
overflow-y: auto;
padding: 10px;
}
.library-category{
margin-bottom: 20px;
}
.library-category h3{
margin: 0 0 10px 0;
padding-bottom: 5px;
border-bottom: 1px solid var(--border-color);
color: var(--text-color);
font-size: 14px;
}
.library-items{
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.library-item{
width: 120px;
padding: 8px;
background: var(--area-background-color);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: pointer;
transition: 0.2s;
}
.library-item:hover{
border-color: var(--background-color-active);
transform: scale(1.02);
}
.library-item img{
width: 100%;
height: 80px;
object-fit: contain;
background: repeating-conic-gradient(#808080 0% 25%, #666 0% 50%) 50% / 10px 10px;
border-radius: 2px;
}
.library-item-name{
margin-top: 5px;
font-size: 11px;
color: var(--text-color);
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.library-item-actions{
display: flex;
gap: 5px;
margin-top: 5px;
}
.library-item-actions button{
flex: 1;
padding: 3px 5px;
font-size: 10px;
cursor: pointer;
border: 1px solid var(--border-color);
border-radius: 3px;
background: var(--section-background-color);
color: var(--text-color);
}
.library-item-actions .insert-btn{
background: var(--background-color-active);
color: var(--text-color-active);
}
.library-item-actions .delete-btn:hover{
background: #dc3545;
color: #fff;
}
.library-dialog .dialog_content{
min-width: 500px;
}
.sidebar_right .label{
display: inline-block;
}
.info .toggle.toggled{
margin-bottom: -3px;
}
.block.details .row{
clear:both;
margin-bottom: 4px;
min-height: 23px;
}
.block.details input[type="number"]{
width: 70px;
padding: 3px 5px;
float: right;
}
.block.details .ui_color_input{
width: 70px;
float: right;
}
.block.details .ui_color_input input{
width: 100%;
height: 23px;
}
.block.details button.ui_toggle_button{
width: 90px;
float: right;
}
.block.details select{
width: calc(100% - 70px);
height: 23px;
float: right;
}
.block.details button{
width: calc(100% - 70px);
height: 23px;
border: 1px solid #444;
}
.block.details button.reset{
position: relative;
width: 25px;
float: right;
margin-right: 3px;
overflow: hidden;
opacity: 0.5;
color: transparent;
}
.block.details button.reset:after{
position: absolute;
content: '';
left:0;
top:0;
bottom:0;
right:0;
background: url(images/icons/refresh.svg) no-repeat center center;
background-size: auto 14px;
filter: var(--menu-icons-filter);
}
.block.details button.active{
background-color: var(--background-color-active);
color: var(--text-color-active);
}
.details-content{
height: 206px;
overflow-y: auto;
}
@media screen and (max-width:700px){
body{
padding-top:50px;
}
.wrapper{
top: 50px;
}
.sidebar_left{
position: absolute;
left: -90px;
background: var(--background);
}
.sidebar_left.active{
box-shadow: -5px 0px 10px 0px rgba(0,0,0,0.75);
left: 0;
z-index: 3;
}
.sidebar_right{
position: absolute;
height: 100%;
right: -210px;
background: var(--background);
}
.sidebar_right.active{
box-shadow: -5px 0px 10px 0px rgba(0,0,0,0.75);
right: 0;
margin-right: 0;
}
}
/* ========== content ======================================================= */
.ruler_left{
display: none;
position: absolute;
left:0;
top: 20px;
background-color: #ccc;
}
.ruler_top{
display: none;
position: absolute;
left: 20px;
top:0;
background-color: #ccc;
}
.middle_area{
position: relative;
-ms-grid-row: 2;
-ms-grid-column: 2;
grid-area: main;
}
.main_wrapper{
position:absolute;
top:0;
right:0;
bottom:0;
left:0;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
.middle_area.has-ruler .main_wrapper{
top: 20px;
left: 20px;
}
.canvas_wrapper{
position:relative;
}
.canvas_wrapper canvas{
position: absolute;
box-sizing: content-box;
font-kerning: normal !important;
}
.loaded .canvas_wrapper canvas{
border: 1px solid var(--border-color);
}
#mouse{
position:absolute;
pointer-events:none;
width:10px;
height:10px;
z-index:10;
}
#mouse.rect{
border:1px solid rgba(0,0,0,0.5);
}
#mouse.circle{
border:1px solid rgba(0,0,0,0.5);
border-radius:50%;
}
.transparent-grid{
width: 100%;
height: 100%;
position: absolute;
pointer-events: none;
/*background: url(images/icons/grid.png) repeat top left;*/
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAQElEQVQ4T2N89+7dfwYigKCgIBGqGBgYRw3EGU6jYYgzaIZAsvn//z9ROeX9+/fE5ZRRA3GG02gY4s4pgz7ZAAAnSWvHPkHXaAAAAABJRU5ErkJggg==') repeat top left;
z-index:1;
image-rendering: pixelated; /* disable antialiasing */
}
.transparent-grid.white{
background:white;
}
.transparent-grid.green{
background: #5be471;
}
.transparent-grid.grey{
background: #dfdfdf;
}
canvas{
position:relative;
z-index:2;
}
#canvas_back{
position: absolute;
background-color:#ffffff;
outline: none;
}
#canvas_grid{
pointer-events:none;
}
.group{
border:1px solid #999999;
margin: 5px 0px 5px 0px;
padding:5px 8px;
}
.flex-container{
display: flex;
flex-wrap: wrap;
}
.flex-container .item{
flex: auto;
margin: 2px 0;
width: 150px;
}
.flex-container .item:empty{
height: 0;
border: none;
}
/* Alertify toast notification styling */
.alertify-notifier .ajs-message {
background-color: #333;
color: #fff;
border-radius: 4px;
padding: 10px 15px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
.alertify-notifier .ajs-message.ajs-success {
background-color: #28a745;
color: #fff;
}
.alertify-notifier .ajs-message.ajs-error {
background-color: #dc3545;
color: #fff;
}
.alertify-notifier .ajs-message.ajs-warning {
background-color: #ffc107;
color: #000;
}
/* Alertify dialog styling - fix text color issues */
.alertify .ajs-dialog {
background-color: #3a3f44;
color: #f4f3f3;
border-radius: 6px;
box-shadow: 0 4px 20px rgba(0,0,0,0.4);
}
.alertify .ajs-header {
background-color: #4a5058;
color: #f4f3f3;
border-bottom: 1px solid #555;
padding: 10px 15px;
font-weight: bold;
}
.alertify .ajs-body {
color: #f4f3f3;
padding: 15px;
}
.alertify .ajs-body .ajs-content {
color: #f4f3f3;
}
.alertify .ajs-footer {
background-color: #3a3f44;
border-top: 1px solid #555;
padding: 10px 15px;
}
.alertify .ajs-footer .ajs-buttons .ajs-button {
background-color: #4a5058;
color: #f4f3f3;
border: 1px solid #666;
border-radius: 4px;
padding: 6px 16px;
margin: 0 4px;
cursor: pointer;
}
.alertify .ajs-footer .ajs-buttons .ajs-button:hover {
background-color: #5a6068;
}
.alertify .ajs-footer .ajs-buttons .ajs-button.ajs-ok {
background-color: #28a745;
border-color: #28a745;
}
.alertify .ajs-footer .ajs-buttons .ajs-button.ajs-ok:hover {
background-color: #218838;
}
.alertify .ajs-input {
background-color: #2a2f34;
color: #f4f3f3;
border: 1px solid #555;
padding: 8px;
border-radius: 4px;
width: 100%;
}
.alertify .ajs-input::placeholder {
color: #999;
}
.effectsPreview{
cursor: pointer;
background-color: #ddd;
}
@media screen and (max-width:550px){
.canvas_wrapper{
margin-left: 0px;
}
}
@media screen and (max-height: 690px){
.sidebar_left{
width: 75px;
}
}
@media screen and (max-height:450px){
.sidebar_left{
width: 88px;
}
}
/* ========== dialogs ======================================================= */
#dialog_color_picker_group {
width: 60%;
}
#dialog_color_channel_group {
width: 40%;
margin-left: 1rem;
}
@media screen and (max-width: 450px) {
#dialog_color_picker .ui_flex_group {
flex-wrap: wrap;
}
#dialog_color_picker_group {
width: 100%;
}
#dialog_color_channel_group {
width: 100%;
margin-left: 0;
margin-top: 1rem;
}
}
+202
View File
@@ -0,0 +1,202 @@
:root {
--menu-dropdown-background-color: #ffffff;
--menu-dropdown-border-color: #49844d;
--menu-dropdown-text-color: #2d2b2b;
--menu-dropdown-text-muted-color: #aaaaaa;
--menu-dropdown-hover-background-color: #adecab;
--menu-dropdown-hover-text-color: #2d2d2d;
--menu-dropdown-divider-color: #e5e5e5;
}
.sr_only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.main_menu {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
}
.main_menu > ul.menu_bar {
display: flex;
flex-direction: row;
list-style: none;
padding: 0;
margin: 0;
height: 30px;
padding-left: 10px;
background: var(--menu-background-color);
}
.main_menu > ul.menu_bar > li {
padding: 0;
overflow: hidden;
height: 100%;
}
.main_menu > ul.menu_bar > li > a {
display: flex;
align-items: center;
font-size: 12px;
color: var(--menu-text-color);
text-decoration: none;
padding: 0 10px;
height: 100%;
}
.main_menu > ul.menu_bar > li > a::-moz-focus-inner {
border: 0;
}
.main_menu > ul.menu_bar > li > a:focus {
outline: none;
box-shadow: 0 -3px var(--menu-dropdown-background-color) inset;
}
.main_menu > ul.menu_bar > li > a:hover {
background: var(--menu-dropdown-hover-background-color);
box-shadow: none;
color: var(--menu-dropdown-hover-text-color);
}
.main_menu > ul.menu_bar > li > a[aria-expanded="true"] {
background: var(--menu-dropdown-background-color);
box-shadow: none;
color: var(--menu-dropdown-text-color);
}
.main_menu > ul.menu_bar > li > a > * {
pointer-events: none;
}
.main_menu > ul.menu_dropdown {
display: flex;
flex-direction: column;
position: fixed;
top: 0;
left: 0;
list-style: none;
padding: 0;
margin: 0;
overflow-x: hidden;
overflow-y: auto;
min-width: 150px;
box-shadow: 0 0 0 1px var(--menu-dropdown-border-color);
background: var(--menu-dropdown-background-color);
}
.main_menu > ul.menu_dropdown > li {
padding: 0;
}
.main_menu > ul.menu_dropdown > li > hr {
background: none;
border: 1px solid var(--menu-dropdown-divider-color);
border-bottom: none;
margin: 0;
}
.main_menu > ul.menu_dropdown > li > a {
display: flex;
flex-direction: row;
align-items: center;
position: relative;
height: 30px;
padding: 0 10px;
font-size: 12px;
line-height: 30px;
text-decoration: none;
color: var(--menu-dropdown-text-color);
}
.main_menu > ul.menu_dropdown > li > ::-moz-focus-inner {
border: 0;
}
.main_menu > ul.menu_dropdown > li > a:focus {
outline: none;
box-shadow: 0 0 0 2px var(--menu-dropdown-hover-background-color) inset;
}
.main_menu > ul.menu_dropdown > li > a:hover {
background: var(--menu-dropdown-hover-background-color);
box-shadow: none;
color: var(--menu-dropdown-hover-text-color);
}
.main_menu > ul.menu_dropdown > li > a[aria-expanded="true"] {
background: var(--menu-dropdown-hover-background-color);
box-shadow: none;
color: var(--menu-dropdown-hover-text-color);
}
.main_menu > ul.menu_dropdown > li > a[aria-haspopup="true"]::after {
position: absolute;
content: ">";
right: 9px;
width: 5px;
transform: scaleY(2);
color: #808080;
}
.main_menu > ul.menu_dropdown > li > a[aria-haspopup="true"] > .name {
margin-right: 8px;
}
.main_menu > ul.menu_dropdown > li > a[target="_blank"]::after {
content: "";
width: 10px;
height: 10px;
margin-left: 5px;
background: url('images/icons/external.png') no-repeat center center;
background-size: auto 8px;
opacity: 0.3;
}
.main_menu > ul.menu_dropdown > li > a > * {
pointer-events: none;
}
.main_menu > ul.menu_dropdown > li > a > .name {
flex-grow: 1;
overflow: hidden;
white-space: nowrap;
}
.main_menu > ul.menu_dropdown > li > a > .shortcut {
flex-shrink: 1;
color: var(--menu-dropdown-text-muted-color);
}
.mobile_menu {
display: none;
position: absolute;
width: 100%;
top: 0;
}
.left_mobile_menu, .right_mobile_menu {
position: absolute;
width: 50px;
height: 50px;
display: block;
top: 0;
z-index: 200;
border: 0;
outline: 0;
cursor: pointer;
background-color: transparent;
}
.left_mobile_menu:after, .right_mobile_menu:after {
position: absolute;
content: '';
left:0;
top:0;
bottom:0;
right:0;
filter: var(--mobile-menu-toggle-filter);
background: url('images/icons/menu.svg') no-repeat center center;
background-size: auto 26px;
}
.left_mobile_menu { left:0; }
.right_mobile_menu { right:0; }
@media screen and (max-width:700px) {
.mobile_menu {
display: block;
}
.main_menu > ul.menu_bar {
height: 50px;
padding-left: 50px;
padding-right: 50px;
}
}
+408
View File
@@ -0,0 +1,408 @@
#popups:not(:empty) {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#popups .popup {
position:fixed;
display:none;
top: 15vh;
left: calc(100vw / 2);
transform: translate(-50%, 0);
background-color: #7A838B;
background-color: var(--block-background-color);
border: 1px solid rgba(0, 0, 0, 0.5);
border: 1px solid var(--border-color);
width: 90vw;
max-width: 500px;
max-height: calc(80vh);
margin:0px auto 0px auto;
padding: 4rem 0 5rem 0;
box-shadow: 0 0 0 4000px rgba(0,0,0,0.3), 0 0 20px rgba(0,0,0,0.5);
z-index: 100;
font-size: 13px;
overflow: hidden;
}
#popups .popup.wide{
max-width: 840px;
}
#popups .popup a{
color: var(--link-color);
}
#popups .popup h2{
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
margin: 0;
height: 4rem;
line-height: 4rem;
padding: 0 1rem;
font-size: 1.8rem;
background-color: rgba(255, 255, 255, 0.3);
background-color: var(--header-background-color);
z-index: 0;
cursor:move;
}
#popups .popup .dialog_content {
overflow-y: auto;
max-height: calc(80vh - 11rem);
padding: 1rem;
}
#popups .popup .buttons{
position: absolute;
background-color: var(--block-background-color);
bottom: 0;
left: 0;
right: 0;
height: 5rem;
line-height: 4rem;
margin: 0;
padding: .5rem 0;
text-align: center;
border-top: 1px solid var(--header-background-color);
z-index: 3;
}
#popups .popup .close{
position: absolute;
right: 0;
top: 0;
min-width: 0;
padding: 5px;
line-height: 0.5;
font-size: 16px;
margin-top: 10px;
margin-right: 10px;
border: none;
background: none;
z-index: 1;
}
#popups .popup input[type="range"]{
margin:0;
width: 100%;
}
#popups .popup table{
box-sizing: border-box;
width: 100%;
}
#popups .popup td, #popups .popup th{
height: 25px;
}
#popups .popup td{
vertical-align: middle;
}
#popups .popup th{
text-align:left;
padding: 5px 5px 5px 0;
width: 130px;
}
#popups .popup textarea{
color: var(--input-text-color);
width:100%;
border:1px solid #393939;
padding-left:5px;
}
#popups .popup .button{
margin: 0 3px;
background-color: rgba(255, 255, 255, 0.2);
background-color: var(--button-background-color);
min-width:60px;
border:1px solid rgba(0, 0, 0, 0.5);
border:1px solid var(--border-color);
padding: 5px 10px;
}
#popups .popup input[type="text"], #popups .popup input[type="number"], #popups .popup textarea{
width:100%;
}
#popups .popup input[type="number"]{
width:100px;
}
#popups .popup input[type="radio"], #popups .popup input[type="checkbox"]{
margin-left: 0;
}
#popups .popup label span{
color:var(--text-color-muted);
}
#popups .popup .checkbox label{
margin-top: 5px;
color:var(--text-color-muted);
}
#popups .popup .preview_container{
margin-top:10px;
margin-bottom:15px;
text-align: center;
}
#popups .popup .preview_canvas_left{
position:relative;
margin:0 5px 5px 0;
border:1px solid #393939;
display: inline-block;
vertical-align: top;
}
#popups .popup .preview_canvas_post_back{
position:absolute;
border:1px solid #393939;
background-color:#ffffff;
}
#popups .popup .preview_canvas_post{
position:relative;
border:1px solid #393939;
}
#popups .popup .canvas_preview_container{
position:relative;
display: inline-block;
vertical-align: top;
}
#popups .popup .radios label{
display: inline-block;
margin-right: 10px;
}
#popups .popup .range_value{
padding-left:10px;
width:50px;
}
#popups .popup .long_text_value{
font-size: 12px;
}
#popups .popup .preview-item-title{
text-align: center;
max-width: 150px;
}
#popups .popup .field_comment{
display: inline-block;
margin-left: 10px;
opacity: 0.5;
}
#popups .popup .selection_card {
background: var(--input-background-color);
display: block;
width: 100%;
padding: 0;
border-bottom: 0.1rem solid var(--input-border-color);
overflow: hidden;
position: relative;
}
#popups .popup .selection_card:first-child {
margin-top: 1rem;
border-radius: var(--input-border-radius) var(--input-border-radius) 0 0;
}
#popups .popup .selection_card:last-child {
border-radius: 0 0 var(--input-border-radius) var(--input-border-radius);
border-bottom: none;
}
#popups .popup .selection_card > input[type="checkbox"] {
flex-grow: 0;
flex-shrink: 0;
margin: 0;
cursor: pointer;
position: absolute;
top: 50%;
left: 1.5rem;
transform: translateY(-50%) scale(1.5);
}
#popups .popup .selection_card > input[type="checkbox"] + label {
display: block;
width: 100%;
flex-grow: 1;
flex-shrink: 1;
margin: 0;
padding: 1rem 0.5rem 1rem 5.5rem;
cursor: pointer;
}
#popups .popup .selection_card > input[type="checkbox"] + label:hover {
background: var(--input-background-color-hover);
}
#popups .popup .selection_card .font_preview {
font-size: 1.6rem;
height: 2.5rem;
line-height: 2.5rem;
white-space: nowrap;
}
#popups .popup .pagination {
display: flex;
text-align: center;
margin: 1rem 0 0 0;
}
#popups .popup .pagination button {
flex-grow: 0;
height: 2.8rem;
line-height: 2.8rem;
border-radius: 0;
margin-left: -1px;
min-width: 3.3rem;
}
#popups .popup .pagination button:first-child {
border-radius: var(--button-border-radius) 0 0 var(--button-border-radius);
margin-left: auto;
}
#popups .popup .pagination button:last-child {
border-radius: 0 var(--button-border-radius) var(--button-border-radius) 0;
margin-right: auto;
}
/* Shape/Library Tabs */
#popups .popup .shape-tabs {
display: flex;
gap: 0;
margin-bottom: 1rem;
border-bottom: 2px solid var(--border-color);
}
#popups .popup .shape-tab {
padding: 0.8rem 1.5rem;
background: transparent;
border: none;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
cursor: pointer;
color: var(--text-color-muted);
font-size: 1rem;
transition: color 0.2s, border-color 0.2s;
}
#popups .popup .shape-tab:hover {
color: var(--text-color);
}
#popups .popup .shape-tab.active {
color: var(--link-color);
border-bottom-color: var(--link-color);
}
#popups .popup .library-loading {
text-align: center;
padding: 2rem;
color: var(--text-color-muted);
}
/* My Library Browser Styles */
#popups .popup .library-browser {
max-height: calc(60vh - 100px);
overflow-y: auto;
}
#popups .popup .library-categories {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
#popups .popup .library-category h3 {
color: var(--text-color);
font-size: 1.4rem;
margin-bottom: 0.8rem;
padding-bottom: 0.4rem;
border-bottom: 1px solid var(--border-color);
}
#popups .popup .library-items {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
#popups .popup .library-item {
display: flex;
flex-direction: column;
align-items: center;
width: 120px;
padding: 0.8rem;
background: var(--input-background-color);
border: 1px solid var(--border-color);
border-radius: 4px;
cursor: pointer;
transition: background 0.2s, border-color 0.2s;
}
#popups .popup .library-item:hover {
background: var(--input-background-color-hover);
border-color: var(--link-color);
}
#popups .popup .library-item img {
width: 100px;
height: 80px;
object-fit: contain;
background: repeating-conic-gradient(#666 0% 25%, #888 0% 50%) 50% / 10px 10px;
border-radius: 2px;
margin-bottom: 0.5rem;
}
#popups .popup .library-item-name {
font-size: 0.85rem;
text-align: center;
color: var(--text-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
margin-bottom: 0.5rem;
}
#popups .popup .library-item-actions {
display: flex;
gap: 0.5rem;
}
#popups .popup .library-item-actions button {
font-size: 0.75rem;
padding: 0.3rem 0.6rem;
background: var(--button-background-color);
border: 1px solid var(--border-color);
border-radius: 3px;
cursor: pointer;
color: var(--text-color);
}
#popups .popup .library-item-actions .insert-btn {
background: #2a6d2a;
}
#popups .popup .library-item-actions .insert-btn:hover {
background: #3a8d3a;
}
#popups .popup .library-item-actions .delete-btn {
background: #6d2a2a;
}
#popups .popup .library-item-actions .delete-btn:hover {
background: #8d3a3a;
}
/* Library empty state */
#popups .popup .library-empty {
text-align: center;
padding: 2rem;
color: var(--text-color-muted);
}
@media screen and (max-width:500px){
#popups .popup {
max-height: calc(80vh - 20px); /* mobile phones has bottom menu */
}
#popups .popup tr{
display: block;
margin-bottom: 10px;
}
#popups .popup td, #popups .popup th{
display: block;
width: 100%;
height: auto;
padding: 5px;
}
#popups .popup th{
padding: 5px 5px 0px 5px;
}
#popups .popup td{
padding: 5px 5px 5px 5px;
}
#popups .popup .range_value{
display: none;
}
}
+34
View File
@@ -0,0 +1,34 @@
@media print{
body{
background:none !important;
background: #fff;
background-color: #fff;
font-family: Arial,Helvetica,Verdana;
width:auto !important;
padding:5px !important;
font-size: 12px;
}
progress,
.menu,
.sidebar_left,
.sidebar_right,
.submenu,
.main_menu{
display: none;
height: 0;
width: 0;
}
.main_wrapper{
margin:0px;
padding:0px;
}
canvas{
border:0px;
position: absolute;
top:0px;
left:0px;
}
.canvas_wrapper canvas{
border:0;
}
}
+230
View File
@@ -0,0 +1,230 @@
:root {
/* original - default */
--background: #666d6f;
--text-color: #f4f3f3;
--text-color-muted: #c1c1c1;
--text-color-red: #e38282;
--text-color-green: #8bdb8b;
--text-color-blue: #a4a4ff;
--link-color: #9ffda5;
--section-background-color: #323a3c;
--area-background-color: #464d4f;
--block-background-color: #464d4f;
--header-background-color: #373d3f;
--button-background-color: #2f3739;
--button-background-color-hover: #75df72;
--button-background-color-active: #4d5153;
--button-shadow-color: rgba(0, 0, 0, 0.3);
--button-text-color-active: #adecab;
--button-border-radius: .4rem;
--button-toggle-background-color: #575f62;
--button-toggle-background-color-hover: #575f62;
--input-background-color: #2f3739;
--input-background-color-hover: #383f44;
--input-text-color: #f4f3f3;
--input-border-color: #0f0f0f;
--input-border-color-active: #70996e;
--input-border-radius: .4rem;
--input-group-border-color: #323a3c;
--menu-background-color: #222;
--menu-icons-filter: invert(1);
--menu-icons-filter-active: none;
--menu-text-color: #cccccc;
--number-input-arrow-width: 2rem;
--background-color-active: #adecab;
--background-color-hover: #575f62;
--text-color-active: #215b2a;
--border-color: #727677;
--scrollbar-track-color: #464d4f;
--scrollbar-thumb-color: #2f3739;
--mobile-menu-toggle-filter: invert(1);
}
body.theme-light{
/* light */
--background: #f9f9fa;
--text-color: #0c0c0d;
--text-color-muted: #444444;
--text-color-red: #bb2424;
--text-color-green: #2b882b;
--text-color-blue: #5454ca;
--link-color: #000080;
--section-background-color: #eaeaea;
--area-background-color: #d9d9d9;
--block-background-color: #eaeaea;
--header-background-color: #dbdbdb;
--button-background-color: #f9f9fa;
--button-background-color-hover: #ddd;
--button-background-color-active: #f3f3f3;
--button-text-color-active: #59aed8;
--button-shadow-color: rgba(0, 0, 0, 0.1);
--button-toggle-background-color: #b7b7b7;
--button-toggle-background-color-hover: #b7b7b7;
--input-background-color: #ffffff;
--input-background-color-hover: #ddd;
--input-text-color: #0c0c0d;
--input-border-color: #ccc;
--input-border-color-active: #59aed8;
--input-group-border-color: #c4c4c4;
--menu-background-color: #eaeaea;
--menu-icons-filter: none;
--menu-icons-filter-active: invert(1);
--menu-text-color: #333333;
--menu-dropdown-hover-background-color: #a3dbf7;
--menu-dropdown-border-color: #15439b;
--background-color-active: #a3dbf7;
--background-color-hover: #c4c4c4;
--text-color-active: #15439b;
--border-color: #c1c1c1;
--scrollbar-track-color: #f9f9fa;
--scrollbar-thumb-color: #919090;
--mobile-menu-toggle-filter: none;
}
body.theme-green{
/* green */
--background: #050702;
--text-color: #acc3a9;
--text-color-muted: #80937d;
--link-color: #9ffda5;
--section-background-color: #1c2e04;
--area-background-color: #3b5f11;
--block-background-color: #3b5f11;
--header-background-color: #2b460f;
--button-background-color: #2e4a0d;
--button-background-color-hover: #58960e;
--button-background-color-active:#2b460f;
--button-text-color-active: #ccc;
--button-toggle-background-color: #243e05;
--button-toggle-background-color-hover: #243e05;
--input-background-color: #ffffff;
--input-background-color-hover: #ddd;
--input-text-color: #0c0c0d;
--input-border-color: #ccc;
--menu-background-color: #1c2e04;
--menu-icons-filter: invert(1);
--menu-icons-filter-active: none;
--menu-text-color: #acc3a9;
--background-color-active: #58960e;
--background-color-hover: #58960e;
--text-color-active: #acc3a9;
--border-color: #4d6b1e;
--scrollbar-track-color: #050702;
--scrollbar-thumb-color: #80937d;
--mobile-menu-toggle-filter: invert(1);
}
*{
box-sizing: border-box;
background-repeat: no-repeat;
}
html {
font-size: 10px; /* Base is 10px for easy REM calculation */
}
body{
margin: 0;
padding: 30px 0 0 0;
background-color: #424F5A;
background: var(--background);
font-size: 1.3rem;
font-family: Arial, Helvetica, sans-serif;
color: var(--text-color);
line-height: 1.4;
font-weight: normal;
overflow: hidden;
}
canvas{
outline: none;
/* disable select canvas */
-webkit-touch-callout: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
}
img{
border: none;
}
td, th{
vertical-align:top;
}
table{
border: 0;
margin: 0;
padding: 0;
vertical-align: baseline;
border-collapse: collapse;
border-spacing: 0;
width:100%;
}
hr{
border-color: rgba(0,0,0,0.3);
border-bottom: 0;
}
input[type="text"], select, input[type="number"], textarea{
background: var(--input-background-color);
border: 1px solid var(--input-border-color);
border-radius: var(--input-border-radius);
color: var(--input-text-color);
padding: 3px 5px;
font-size: 13px;
}
input:disabled {
opacity: 0.3;
}
select{
padding: 2px 4px;
}
input[type="range"]{
margin-left: 0;
width:100%;
}
button, input[type="button"]{
border-radius: var(--button-border-radius);
box-shadow: 0 1px 2px 0 var(--button-shadow-color), 0 1px 0 0 rgba(255, 255, 255, 0.1) inset;
cursor: pointer;
border: 1px solid var(--border-color);
background-color: var(--button-background-color);
color: var(--text-color);
}
button:hover, input[type="button"]:hover{
background-color: var(--button-background-color-hover);
}
button:disabled, input[type="button"]:disabled{
visibility:hidden;
}
button[aria-pressed="true"], input[type="button"][aria-pressed="true"]{
background-color: var(--button-background-color-active);
color: var(--button-text-color-active);
box-shadow: 0 1px 2px 0 var(--button-shadow-color), 0 1px 1px 1.5px rgba(58, 40, 40, 0.1) inset, 0 -1px 0 0 var(--button-text-color-active) inset;
}
button[aria-pressed="true"]:hover, input[type="button"][aria-pressed="true"]:hover{
background-color: var(--button-background-color-hover);
}
button.ui_toggle_button:hover{
background-color: var(--button-toggle-background-color-hover);
}
label{
display: inline-block;
vertical-align: top;
margin-top: 7px;
}
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
::-webkit-scrollbar-track-piece {
background: rgba(0,0,0,0.3);
}
::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.6);
}
@supports (zoom:2) {
input[type="radio"], input[type=checkbox]{
zoom: 1.5;
}
}
@supports not (zoom:2) {
input[type="radio"], input[type=checkbox]{
transform: scale(1.5);
transform-origin: left center;
margin: 8px 12px 8px 0;
}
}
+81
View File
@@ -0,0 +1,81 @@
/* Common input label sizes */
.label_width_character {
width: 100%;
max-width: 2.88rem;
overflow: hidden;
flex-shrink: 0;
}
.label_width_small {
width: 100%;
max-width: 6.4rem;
overflow: hidden;
}
.label_width_medium {
width: 100%;
max-width: 10.4rem;
overflow: hidden;
}
/* Font color utility */
.text_red { color: var(--text-color-red); }
.text_green { color: var(--text-color-green); }
.text_blue { color: var(--text-color-blue); }
.text_muted { color: var(--text-color-muted); }
/*
Size inputs based on the number of "w" characters that could fit in the input. "w" is usually the widest character.
This is a rough estimate since all characters vary in width. For example an input with numbers
usually fits way more characters than an input with letters.
"cw" means character width
*/
.input_cw_1, .input_cw_2, .input_cw_3, .input_cw_4, .input_cw_5,
.input_cw_6, .input_cw_7, .input_cw_8, .input_cw_9, .input_cw_10
.input_cw_11, .input_cw_12, .input_cw_13, .input_cw_14, .input_cw_15 {
width: 100%;
}
.input_cw_1 { max-width: 2.25rem; }
.input_cw_2 { max-width: 3.25rem; }
.input_cw_3 { max-width: 4.25rem; }
.input_cw_4 { max-width: 5.25rem; }
.input_cw_5 { max-width: 6.25rem; }
.input_cw_6 { max-width: 7.25rem; }
.input_cw_7 { max-width: 8.25rem; }
.input_cw_8 { max-width: 9.25rem; }
.input_cw_9 { max-width: 10.25rem; }
.input_cw_10 { max-width: 11.25rem; }
.input_cw_11 { max-width: 12.25rem; }
.input_cw_12 { max-width: 13.25rem; }
.input_cw_13 { max-width: 14.25rem; }
.input_cw_14 { max-width: 15.25rem; }
.input_cw_15 { max-width: 16.25rem; }
input[type="number"].input_cw_1 { max-width: 4.25rem; }
input[type="number"].input_cw_2 { max-width: 5.25rem; }
input[type="number"].input_cw_3 { max-width: 6.25rem; }
input[type="number"].input_cw_4 { max-width: 7.25rem; }
input[type="number"].input_cw_5 { max-width: 8.25rem; }
input[type="number"].input_cw_6 { max-width: 9.25rem; }
input[type="number"].input_cw_7 { max-width: 10.25rem; }
input[type="number"].input_cw_8 { max-width: 11.25rem; }
input[type="number"].input_cw_9 { max-width: 12.25rem; }
input[type="number"].input_cw_10 { max-width: 13.25rem; }
input[type="number"].input_cw_11 { max-width: 14.25rem; }
input[type="number"].input_cw_12 { max-width: 15.25rem; }
input[type="number"].input_cw_13 { max-width: 16.25rem; }
input[type="number"].input_cw_14 { max-width: 17.25rem; }
input[type="number"].input_cw_15 { max-width: 18.25rem; }
.ui_number_input.input_cw_1 { max-width: calc(2.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_2 { max-width: calc(3.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_3 { max-width: calc(4.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_4 { max-width: calc(5.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_5 { max-width: calc(6.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_6 { max-width: calc(7.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_7 { max-width: calc(8.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_8 { max-width: calc(9.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_9 { max-width: calc(10.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_10 { max-width: calc(11.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_11 { max-width: calc(12.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_12 { max-width: calc(13.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_13 { max-width: calc(14.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_14 { max-width: calc(15.25rem + var(--number-input-arrow-width)); }
.ui_number_input.input_cw_15 { max-width: calc(16.25rem + var(--number-input-arrow-width)); }
@@ -0,0 +1,5 @@
# Managing Undo History with Actions
More information on wiki page:
https://github.com/viliusle/miniPaint/wiki/Undo-Redo-system
@@ -0,0 +1,145 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
export class Activate_tool_action extends Base_action {
/**
* Groups multiple actions together in the undo/redo history, runs them all at once.
*/
constructor(key, ignore_same_tool) {
super('activate_tool', 'Activate Tool');
this.ignore_same_tool = !!ignore_same_tool;
this.key = key;
this.old_key = null;
this.tool_leave_actions = null;
this.tool_activate_actions = null;
}
async do() {
super.do();
const key = this.key;
this.old_key = app.GUI.GUI_tools.active_tool;
if (this.key !== this.old_key || this.ignore_same_tool) {
//reset last
document.querySelector('#tools_container .' + this.old_key).classList.remove("active");
//send exit event to old previous tool
if (config.TOOL.on_leave != undefined) {
var moduleKey = config.TOOL.name;
var functionName = config.TOOL.on_leave;
this.tool_leave_actions = app.GUI.GUI_tools.tools_modules[moduleKey].object[functionName]();
if (this.tool_leave_actions) {
for (let action of this.tool_leave_actions) {
await action.do();
}
}
}
//change active
app.GUI.GUI_tools.active_tool = key;
document.querySelector('#tools_container .' + app.GUI.GUI_tools.active_tool)
.classList.add("active");
for (let i in config.TOOLS) {
if (config.TOOLS[i].name == app.GUI.GUI_tools.active_tool) {
config.TOOL = config.TOOLS[i];
}
}
//check module
if (app.GUI.GUI_tools.tools_modules[key] == undefined) {
alertify.error('Tools class not found: ' + key);
return;
}
//set default cursor
const mainWrapper = document.getElementById('main_wrapper');
const defaultCursor = config.TOOL && config.TOOL.name === 'text' ? 'text' : 'default';
if (mainWrapper.style.cursor != defaultCursor) {
mainWrapper.style.cursor = defaultCursor;
}
app.GUI.GUI_tools.show_action_attributes();
app.GUI.GUI_tools.Helper.setCookie('active_tool', app.GUI.GUI_tools.active_tool);
}
//send activate event to new tool
if (config.TOOL.on_activate != undefined) {
var moduleKey = config.TOOL.name;
var functionName = config.TOOL.on_activate;
this.tool_activate_actions = app.GUI.GUI_tools.tools_modules[moduleKey].object[functionName]();
if (this.tool_activate_actions) {
for (let action of this.tool_activate_actions) {
await action.do();
}
}
}
config.need_render = true;
}
async undo() {
super.undo();
// Undo activate actions
if (this.tool_activate_actions) {
for (let action of this.tool_activate_actions) {
await action.undo();
action.free();
}
this.tool_activate_actions = null;
}
//reset last
document.querySelector('#tools_container .' + this.key)
.classList.remove("active");
//change active
app.GUI.GUI_tools.active_tool = this.old_key;
document.querySelector('#tools_container .' + app.GUI.GUI_tools.active_tool)
.classList.add("active");
for (let i in config.TOOLS) {
if (config.TOOLS[i].name == app.GUI.GUI_tools.active_tool) {
config.TOOL = config.TOOLS[i];
}
}
app.GUI.GUI_tools.show_action_attributes();
app.GUI.GUI_tools.Helper.setCookie('active_tool', app.GUI.GUI_tools.active_tool);
//set default cursor
const mainWrapper = document.getElementById('main_wrapper');
const defaultCursor = config.TOOL && config.TOOL.name === 'text' ? 'text' : 'default';
if (mainWrapper.style.cursor != defaultCursor) {
mainWrapper.style.cursor = defaultCursor;
}
// Undo leave actions
if (this.tool_leave_actions) {
for (let action of this.tool_leave_actions) {
await action.undo();
action.free();
}
this.tool_leave_actions = null;
}
config.need_render = true;
}
free() {
if (this.tool_activate_actions) {
for (let action of this.tool_activate_actions) {
action.free();
}
this.tool_activate_actions = null;
}
if (this.tool_leave_actions) {
for (let action of this.tool_leave_actions) {
action.free();
}
this.tool_leave_actions = null;
}
}
}
@@ -0,0 +1,67 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Add_layer_filter_action extends Base_action {
/**
* register new live filter
*
* @param {int} layer_id
* @param {string} name
* @param {object} params
*/
constructor(layer_id, name, params, filter_id) {
super('add_layer_filter', 'Add Layer Filter');
if (layer_id == null)
layer_id = config.layer.id;
this.layer_id = parseInt(layer_id);
this.name = name;
this.params = params;
this.filter_id = filter_id;
this.reference_layer = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
var filter = {
id: this.filter_id,
name: this.name,
params: this.params,
};
if(this.filter_id) {
//update
for(var i in this.reference_layer.filters) {
if(this.reference_layer.filters[i].id == this.filter_id){
this.reference_layer.filters[i] = filter;
break;
}
}
}
else{
//insert
filter.id = Math.floor(Math.random() * 999999999) + 1; // A good UUID library would
this.reference_layer.filters.push(filter);
}
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reference_layer) {
this.reference_layer.filters.pop();
this.reference_layer = null;
}
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
free() {
this.reference_layer = null;
this.params = null;
}
}
@@ -0,0 +1,100 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
import Tools_settings_class from './../modules/tools/settings.js';
export class Autoresize_canvas_action extends Base_action {
/**
* autoresize canvas to layer size, based on dimensions, up - always, if 1 layer - down.
*
* @param {int} width
* @param {int} height
* @param {int} layer_id
* @param {boolean} can_automate
*/
constructor(width, height, layer_id, can_automate = true, ignore_same_size = false) {
super('autoresize_canvas', 'Auto-resize Canvas');
this.Tools_settings = new Tools_settings_class();
this.width = width;
this.height = height;
this.layer_id = layer_id;
this.can_automate = can_automate;
this.ignore_same_size = ignore_same_size;
this.old_config_width = null;
this.old_config_height = null;
}
async do() {
super.do();
const width = this.width;
const height = this.height;
const can_automate = this.can_automate;
let need_fit = false;
let new_config_width = config.WIDTH;
let new_config_height = config.HEIGHT;
var enable_autoresize = this.Tools_settings.get_setting('enable_autoresize');
if(enable_autoresize == false){
return;
}
// Resize up
if (width > new_config_width || height > new_config_height) {
const wrapper = document.getElementById('main_wrapper');
const page_w = wrapper.clientWidth;
const page_h = wrapper.clientHeight;
if (width > page_w || height > page_h) {
need_fit = true;
}
if (width > new_config_width)
new_config_width = parseInt(width);
if (height > new_config_height)
new_config_height = parseInt(height);
}
// Resize down
if (config.layers.length == 1 && can_automate !== false) {
if (width < new_config_width)
new_config_width = parseInt(width);
if (height < new_config_height)
new_config_height = parseInt(height);
}
if (new_config_width !== config.WIDTH || new_config_height !== height) {
this.old_config_width = config.WIDTH;
this.old_config_height = config.HEIGHT;
config.WIDTH = new_config_width;
config.HEIGHT = new_config_height;
app.GUI.prepare_canvas();
} else if (!this.ignore_same_size) {
throw new Error('Aborted - Resize not necessary')
}
// Fit zoom when after short pause
// @todo - remove setTimeout
if (need_fit == true) {
await new Promise((resolve) => {
window.setTimeout(() => {
app.GUI.GUI_preview.zoom_auto();
resolve();
}, 100);
});
}
}
async undo() {
super.undo();
if (this.old_config_width != null) {
config.WIDTH = this.old_config_width;
}
if (this.old_config_height != null) {
config.HEIGHT = this.old_config_height;
}
if (this.old_config_width != null || this.old_config_height != null) {
app.GUI.prepare_canvas();
}
this.old_config_width = null;
this.old_config_height = null;
}
}
+19
View File
@@ -0,0 +1,19 @@
export class Base_action {
constructor(action_id, action_description) {
this.action_id = action_id;
this.action_description = action_description;
this.is_done = false;
this.memory_estimate = 0; // Estimate of how much memory will be freed when the free() method is called (in bytes)
this.database_estimate = 0; // Estimate of how much database space will be freed when the free() method is called (in bytes)
}
do() {
this.is_done = true;
}
undo() {
this.is_done = false;
}
free() {
// Override if need to run tasks to free memory when action is discarded from history
}
}
@@ -0,0 +1,59 @@
import config from '../config.js';
import { Base_action } from './base.js';
export class Bundle_action extends Base_action {
/**
* Groups multiple actions together in the undo/redo history, runs them all at once.
*/
constructor(bundle_id, bundle_name, actions_to_do) {
super(bundle_id, bundle_name);
this.actions_to_do = actions_to_do;
}
async do() {
super.do();
let error = null;
let i = 0;
this.memory_estimate = 0;
this.database_estimate = 0;
for (i = 0; i < this.actions_to_do.length; i++) {
try {
await this.actions_to_do[i].do();
this.memory_estimate += this.actions_to_do[i].memory_estimate;
this.database_estimate += this.actions_to_do[i].database_estimate;
} catch (e) {
error = e;
break;
}
}
// One of the actions aborted, undo all previous actions.
if (error) {
for (i--; i >= 0; i--) {
await this.actions_to_do[i].undo();
}
throw error;
}
config.need_render = true;
}
async undo() {
super.undo();
this.memory_estimate = 0;
this.database_estimate = 0;
for (let i = this.actions_to_do.length - 1; i >= 0; i--) {
await this.actions_to_do[i].undo();
this.memory_estimate += this.actions_to_do[i].memory_estimate;
this.database_estimate += this.actions_to_do[i].database_estimate;
}
config.need_render = true;
}
free() {
if (this.actions_to_do) {
for (let action of this.actions_to_do) {
action.free();
}
this.actions_to_do = null;
}
}
}
@@ -0,0 +1,82 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Clear_layer_action extends Base_action {
/**
* clear layer data
*
* @param {int} layer_id
*/
constructor(layer_id) {
super('clear_layer', 'Clear Layer');
this.layer_id = parseInt(layer_id);
this.update_layer_action = null;
this.delete_layer_settings_action = null;
}
async do() {
super.do();
let layer = app.Layers.get_layer(this.layer_id);
if (!layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
let new_settings = {
x: 0,
y: 0,
width: 0,
height: 0,
visible: true,
opacity: 100,
composition: null,
rotate: 0,
data: null,
params: {},
status: null,
render_function: null,
type: null
};
if (layer.type == 'image') {
//clean image
new_settings.link = null;
}
this.update_layer_action = new app.Actions.Update_layer_action(this.layer_id, new_settings);
await this.update_layer_action.do();
let delete_setting_names = [];
for (let prop_name in layer) {
//remove private attributes
if (prop_name[0] == '_') {
delete_setting_names.push(prop_name);
}
}
if (delete_setting_names.length > 0) {
this.delete_layer_settings_action = new app.Actions.Delete_layer_settings_action(this.layer_id, delete_setting_names);
await this.delete_layer_settings_action.do();
}
}
async undo() {
super.undo();
if (this.delete_layer_settings_action) {
await this.delete_layer_settings_action.undo();
this.delete_layer_settings_action.free();
this.delete_layer_settings_action = null;
}
if (this.update_layer_action) {
await this.update_layer_action.undo();
this.update_layer_action.free();
this.update_layer_action = null;
}
}
free() {
if (this.update_layer_action) {
this.update_layer_action.free();
this.update_layer_action = null;
}
if (this.delete_layer_settings_action) {
this.delete_layer_settings_action.free();
this.delete_layer_settings_action = null;
}
}
}
@@ -0,0 +1,60 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Delete_layer_filter_action extends Base_action {
/**
* delete live filter
*
* @param {int} layer_id
* @param {string} filter_id
*/
constructor(layer_id, filter_id) {
super('delete_layer_filter', 'Delete Layer Filter');
if (layer_id == null)
layer_id = config.layer.id;
this.layer_id = parseInt(layer_id);
this.filter_id = filter_id;
this.reference_layer = null;
this.filter_remove_index = null;
this.old_filter = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
this.old_filter = null;
for (let i in this.reference_layer.filters) {
if (this.reference_layer.filters[i].id == this.filter_id) {
this.filter_remove_index = i;
this.old_filter = this.reference_layer.filters.splice(i, 1)[0];
break;
}
}
if (!this.old_filter) {
throw new Error('Aborted - filter with specified id doesn\'t exist in layer');
}
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reference_layer && this.old_filter) {
this.reference_layer.filters.splice(this.filter_remove_index, 0, this.old_filter);
}
this.reference_layer = null;
this.old_filter = null;
this.filter_remove_index = null;
config.need_render = true;
app.GUI.GUI_layers.render_layers();
}
free() {
this.reference_layer = null;
this.old_filter = null;
}
}
@@ -0,0 +1,50 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Delete_layer_settings_action extends Base_action {
/**
* Deletes the specified settings in a layer
*
* @param {int} layer_id
* @param {array} setting_names
*/
constructor(layer_id, setting_names) {
super('delete_layer_settings', 'Delete Layer Settings');
this.layer_id = parseInt(layer_id);
this.setting_names = setting_names;
this.reference_layer = null;
this.old_settings = {};
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
for (let name in this.setting_names) {
this.old_settings[name] = this.reference_layer[name];
delete this.reference_layer[name];
}
config.need_render = true;
}
async undo() {
super.undo();
if (this.reference_layer) {
for (let i in this.old_settings) {
this.reference_layer[i] = this.old_settings[i];
}
this.old_settings = {};
}
this.reference_layer = null;
config.need_render = true;
}
free() {
this.setting_names = null;
this.reference_layer = null;
this.old_settings = null;
}
}
@@ -0,0 +1,115 @@
import config from '../config.js';
import app from './../app.js';
import { Base_action } from './base.js';
export class Delete_layer_action extends Base_action {
/**
* removes layer
*
* @param {int} id
* @param {boolean} force - Force to delete first layer?
*/
constructor(layer_id, force) {
super('delete_layer', 'Delete Layer');
this.layer_id = parseInt(layer_id);
this.force = force || false;
this.insert_layer_action = null;
this.select_layer_action = null;
this.delete_index = null;
this.deleted_layer = null;
}
async do() {
super.do();
const id = this.layer_id;
const force = this.force;
// Determine if there is a layer to delete, abort if not
for (var i in config.layers) {
if (config.layers[i].id == id) {
this.delete_index = i;
}
}
if (this.delete_index === null) {
throw new Error('Aborted - Layer to delete not found');
}
if (config.layers.length == 1 && (force == undefined || force == false)) {
// Only 1 layer left
if (config.layer.type == null) {
//STOP
throw new Error('Aborted - Will not delete last layer');
}
else {
// Delete it, but before that - create new empty layer
this.insert_layer_action = new app.Actions.Insert_layer_action();
this.insert_layer_action.do();
}
}
if (config.layers.length > 1 && config.layer.id == id) {
// Select next or previous layer
try {
const select_action = new app.Actions.Select_next_layer_action(id);
await select_action.do();
this.select_layer_action = select_action;
} catch (error) {
const select_action = new app.Actions.Select_previous_layer_action(id);
await select_action.do();
this.select_layer_action = select_action;
}
}
// Remove layer from list
this.deleted_layer = config.layers.splice(this.delete_index, 1)[0];
// Estimate memory
if (this.deleted_layer.link && this.deleted_layer.link.src && typeof this.deleted_layer.link.src === 'string') {
this.memory_estimate = new Blob([this.deleted_layer.link.src]).size;
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.deleted_layer) {
config.layers.splice(this.delete_index, 0, this.deleted_layer);
this.delete_index = null;
this.deleted_layer = null;
}
if (this.select_layer_action) {
await this.select_layer_action.undo();
this.select_layer_action.free();
this.select_layer_action = null;
}
if (this.insert_layer_action) {
await this.insert_layer_action.undo();
this.insert_layer_action.free();
this.insert_layer_action = null;
}
// Estimate memory
this.memory_estimate = 0;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
if (this.deleted_layer) {
delete this.deleted_layer.link;
delete this.deleted_layer.data;
}
if (this.insert_layer_action) {
this.insert_layer_action.free();
this.insert_layer_action = null;
}
if (this.select_layer_action) {
this.select_layer_action.free();
this.select_layer_action = null;
}
this.deleted_layer = null;
}
}
@@ -0,0 +1,26 @@
export { Activate_tool_action } from './activate-tool.js';
export { Add_layer_filter_action } from './add-layer-filter.js';
export { Autoresize_canvas_action } from './autoresize-canvas.js';
export { Bundle_action } from './bundle.js';
export { Clear_layer_action } from './clear-layer.js';
export { Delete_layer_action } from './delete-layer.js';
export { Delete_layer_filter_action } from './delete-layer-filter.js';
export { Delete_layer_settings_action } from './delete-layer-settings.js';
export { Init_canvas_zoom_action } from './init-canvas-zoom.js';
export { Insert_layer_action } from './insert-layer.js';
export { Prepare_canvas_action } from './prepare-canvas.js';
export { Reorder_layer_action } from './reorder-layer.js';
export { Reset_layers_action } from './reset-layers.js';
export { Refresh_action_attributes_action } from './refresh-action-attributes.js';
export { Refresh_layers_gui_action } from './refresh-layers-gui.js';
export { Reset_selection_action } from './reset-selection.js';
export { Select_layer_action } from './select-layer.js';
export { Select_next_layer_action } from './select-next-layer.js';
export { Select_previous_layer_action } from './select-previous-layer.js';
export { Set_object_property_action } from './set-object-property.js';
export { Set_selection_action } from './set-selection.js';
export { Stop_animation_action } from './stop-animation.js';
export { Toggle_layer_visibility_action } from './toggle-layer-visibility.js';
export { Update_config_action } from './update-config.js';
export { Update_layer_image_action } from './update-layer-image.js';
export { Update_layer_action } from './update-layer.js';
@@ -0,0 +1,45 @@
import app from '../app.js';
import config from '../config.js';
import zoomView from '../libs/zoomView.js';
import { Base_action } from './base.js';
export class Init_canvas_zoom_action extends Base_action {
/**
* Resets the canvas
*/
constructor() {
super('init_canvas_zoom', 'Initialize Canvas Zoom');
this.old_bounds = null;
this.old_context = null;
this.old_stable_dimensions = null;
}
async do() {
super.do();
this.old_bounds = zoomView.getBounds();
this.old_context = zoomView.getContext();
this.old_stable_dimensions = app.Layers.stable_dimensions;
zoomView.setBounds(0, 0, config.WIDTH, config.HEIGHT);
zoomView.setContext(app.Layers.ctx);
app.Layers.stable_dimensions = [
config.WIDTH,
config.HEIGHT
];
}
async undo() {
super.undo();
zoomView.setBounds(this.old_bounds.top, this.old_bounds.left, this.old_bounds.right, this.old_bounds.bottom);
zoomView.setContext(this.old_context);
app.Layers.stable_dimensions = this.old_stable_dimensions;
this.old_bounds = null;
this.old_context = null;
this.old_stable_dimensions = null;
}
free() {
this.old_bounds = null;
this.old_context = null;
this.old_stable_dimensions = null;
}
}
@@ -0,0 +1,214 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
export class Insert_layer_action extends Base_action {
/**
* Creates new layer
*
* @param {object} settings
* @param {boolean} can_automate
*/
constructor(settings, can_automate = true) {
super('insert_layer', 'Insert Layer');
this.settings = settings;
this.can_automate = can_automate;
this.previous_auto_increment = null;
this.previous_selected_layer = null;
this.inserted_layer_id = null;
this.update_layer_action = null;
this.delete_layer_action = null;
this.autoresize_canvas_action = null;
}
async do() {
super.do();
this.previous_auto_increment = app.Layers.auto_increment;
this.previous_selected_layer = config.layer;
let autoresize_as = null;
// Default data
const layer = {
id: app.Layers.auto_increment,
parent_id: 0,
name: config.TOOL.name.charAt(0).toUpperCase() + config.TOOL.name.slice(1) + ' #' + app.Layers.auto_increment,
type: null,
link: null,
x: 0,
y: 0,
width: null,
width_original: null,
height: null,
height_original: null,
visible: true,
is_vector: false,
hide_selection_if_active: false,
opacity: 100,
order: app.Layers.auto_increment,
composition: 'source-over',
rotate: 0,
data: null,
params: {},
status: null,
color: config.COLOR,
filters: [],
render_function: null,
};
// Build data
for (let i in this.settings) {
if (typeof layer[i] == "undefined" && !i.startsWith('_')) {
alertify.error('Error: wrong key: ' + i);
continue;
}
layer[i] = this.settings[i];
}
// Prepare image
let image_load_promise;
if (layer.type == 'image') {
if(layer.name.toLowerCase().indexOf('.svg') == layer.name.length - 4){
// We have svg
layer.is_vector = true;
}
if (config.layers.length == 1 && (config.layer.width == 0 || config.layer.width === null)
&& (config.layer.height == 0 || config.layer.height === null) && config.layer.data == null) {
// Remove first empty layer
this.delete_layer_action = new app.Actions.Delete_layer_action(config.layer.id, true);
await this.delete_layer_action.do();
}
if (layer.link == null) {
if (typeof layer.data == 'object') {
// Load actual image
if (layer.width == 0 || layer.width === null)
layer.width = layer.data.width;
if (layer.height == 0 || layer.height === null)
layer.height = layer.data.height;
layer.link = layer.data.cloneNode(true);
layer.link.onload = function () {
config.need_render = true;
};
layer.data = null;
autoresize_as = [layer.width, layer.height, null, true, true];
//need_autoresize = true;
}
else if (typeof layer.data == 'string') {
image_load_promise = new Promise((resolve, reject) => {
// Try loading as imageData
layer.link = new Image();
layer.link.onload = () => {
// Update dimensions
if (layer.width == 0 || layer.width === null)
layer.width = layer.link.width;
if (layer.height == 0 || layer.height === null)
layer.height = layer.link.height;
if (layer.width_original == null)
layer.width_original = layer.width;
if (layer.height_original == null)
layer.height_original = layer.height;
// Free data
layer.data = null;
autoresize_as = [layer.width, layer.height, layer.id, this.can_automate, true];
config.need_render = true;
resolve();
};
layer.link.onerror = (error) => {
resolve(error);
alertify.error('Sorry, image could not be loaded.');
};
layer.link.src = layer.data;
layer.link.crossOrigin = "Anonymous";
});
}
else {
alertify.error('Error: can not load image.');
}
}
}
if (this.settings != undefined && config.layers.length > 0
&& (config.layer.width == 0 || config.layer.width === null) && (config.layer.height == 0 || config.layer.height === null)
&& config.layer.data == null && layer.type != 'image' && this.can_automate !== false) {
// Update existing layer, because it's empty
this.update_layer_action = new app.Actions.Update_layer_action(config.layer.id, layer);
await this.update_layer_action.do();
}
else {
// Create new layer
config.layers.push(layer);
config.layer = app.Layers.get_layer(layer.id);
app.Layers.auto_increment++;
if (config.layer == null) {
config.layer = config.layers[0];
}
this.inserted_layer_id = layer.id;
}
if (layer.id >= app.Layers.auto_increment)
app.Layers.auto_increment = layer.id + 1;
if (image_load_promise) {
await image_load_promise;
}
if (autoresize_as) {
this.autoresize_canvas_action = new app.Actions.Autoresize_canvas_action(...autoresize_as);
try {
await this.autoresize_canvas_action.do();
} catch(error) {
this.autoresize_canvas_action = null;
}
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
app.Layers.auto_increment = this.previous_auto_increment;
if (this.autoresize_canvas_action) {
await this.autoresize_canvas_action.undo();
this.autoresize_canvas_action = null;
}
if (this.inserted_layer_id) {
config.layers.pop();
this.inserted_layer_id = null;
}
if (this.update_layer_action) {
await this.update_layer_action.undo();
this.update_layer_action.free();
this.update_layer_action = null;
}
if (this.delete_layer_action) {
await this.delete_layer_action.undo();
this.delete_layer_action.free();
this.delete_layer_action = null;
}
config.layer = this.previous_selected_layer;
this.previous_selected_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
if (this.delete_layer_action) {
this.delete_layer_action.free();
this.delete_layer_action = null;
}
if (this.update_layer_action) {
this.update_layer_action.free();
this.update_layer_action = null;
}
this.previous_selected_layer = null;
}
}
@@ -0,0 +1,29 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Prepare_canvas_action extends Base_action {
/**
* Resizes/renders the canvas at the specified step. Usually used on both sides of a config update action.
*
* @param {boolean} call_when
*/
constructor(call_when = 'undo') {
super('prepare_canvas', 'Prepare Canvas');
this.call_when = call_when;
}
async do() {
super.do();
if (this.call_when === 'do') {
app.GUI.prepare_canvas();
}
}
async undo() {
super.undo();
if (this.call_when === 'undo') {
app.GUI.prepare_canvas();
}
}
}
@@ -0,0 +1,29 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Refresh_action_attributes_action extends Base_action {
/**
* Resizes/renders the canvas at the specified step. Usually used on both sides of a config update action.
*
* @param {boolean} call_when
*/
constructor(call_when = 'undo') {
super('refresh_action_attributes', 'Refresh Action Attributes');
this.call_when = call_when;
}
async do() {
super.do();
if (this.call_when === 'do') {
app.GUI.GUI_tools.show_action_attributes();
}
}
async undo() {
super.undo();
if (this.call_when === 'undo') {
app.GUI.GUI_tools.show_action_attributes();
}
}
}
@@ -0,0 +1,29 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Refresh_layers_gui_action extends Base_action {
/**
* Resizes/renders the canvas at the specified step. Usually used on both sides of a config update action.
*
* @param {boolean} call_when
*/
constructor(call_when = 'undo') {
super('refresh_gui', 'Refresh GUI');
this.call_when = call_when;
}
async do() {
super.do();
if (this.call_when === 'do') {
app.Layers.refresh_gui();
}
}
async undo() {
super.undo();
if (this.call_when === 'undo') {
app.Layers.refresh_gui();
}
}
}
@@ -0,0 +1,64 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Reorder_layer_action extends Base_action {
/**
* Reorder layer up or down in the layer stack
*
* @param {int} layer_id
* @param {int} direction
*/
constructor(layer_id, direction) {
super('reorder_layer', 'Reorder Layer');
this.layer_id = parseInt(layer_id);
this.direction = direction;
this.reference_layer = null;
this.reference_target = null;
this.old_layer_order = null;
this.old_target_order = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
if (this.direction < 0) {
this.reference_target = app.Layers.find_previous(this.layer_id);
}
else {
this.reference_target = app.Layers.find_next(this.layer_id);
}
if (!this.reference_target) {
throw new Error('Aborted - layer has nowhere to move');
}
this.old_layer_order = this.reference_layer.order;
this.old_target_order = this.reference_target.order;
this.reference_layer.order = this.old_target_order;
this.reference_target.order = this.old_layer_order;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reference_layer) {
this.reference_layer.order = this.old_layer_order;
this.reference_layer = null;
}
if (this.reference_target) {
this.reference_target.order = this.old_target_order;
this.reference_target = null;
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
this.reference_layer = null;
this.reference_target = null;
}
}
@@ -0,0 +1,66 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Reset_layers_action extends Base_action {
/*
* removes all layers
*/
constructor(auto_insert) {
super('reset_layers', 'Reset Layers');
this.auto_insert = auto_insert;
this.previous_auto_increment = null;
this.delete_actions = null;
this.insert_action = null;
}
async do() {
super.do();
const auto_insert = this.auto_insert;
this.previous_auto_increment = app.Layers.auto_increment;
this.delete_actions = [];
for (let i = config.layers.length - 1; i >= 0; i--) {
const delete_action = new app.Actions.Delete_layer_action(config.layers[i].id, true);
await delete_action.do();
this.delete_actions.push(delete_action);
}
app.Layers.auto_increment = 1;
if (auto_insert != undefined && auto_insert === true) {
const settings = {};
this.insert_action = new app.Actions.Insert_layer_action(settings);
await this.insert_action.do();
}
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.insert_action) {
await this.insert_action.undo();
this.insert_action.free();
this.insert_action = null;
}
for (let i = this.delete_actions.length - 1; i >= 0; i--) {
await this.delete_actions[i].undo();
this.delete_actions[i].free();
}
app.Layers.auto_increment = this.previous_auto_increment;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
if (this.insert_action) {
this.insert_action.free();
this.insert_action = null;
}
if (this.delete_actions) {
for (let action of this.delete_actions) {
action.free();
}
this.delete_actions = null;
}
}
}
@@ -0,0 +1,57 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Reset_selection_action extends Base_action {
/**
* Sets the selection to empty
*
* @prop {object} [mirror_selection_settings] - Optional object to also set to an empty selection object
*/
constructor(mirror_selection_settings) {
super('reset_selection', 'Reset Selection');
this.mirror_selection_settings = mirror_selection_settings;
this.settings_reference = null;
this.old_settings_data = null;
}
async do() {
super.do();
this.settings_reference = app.Layers.Base_selection.find_settings();
this.old_settings_data = JSON.parse(JSON.stringify(this.settings_reference.data));
this.settings_reference.data = {
x: null,
y: null,
width: null,
height: null
}
if (this.mirror_selection_settings) {
this.mirror_selection_settings.x = null;
this.mirror_selection_settings.y = null;
this.mirror_selection_settings.width = null;
this.mirror_selection_settings.height = null;
}
config.need_render = true;
}
async undo() {
super.undo();
if (this.old_settings_data) {
for (let prop of ['x', 'y', 'width', 'height']) {
this.settings_reference.data[prop] = this.old_settings_data[prop];
if (this.mirror_selection_settings) {
this.mirror_selection_settings[prop] = this.old_settings_data[prop];
}
}
}
this.settings_reference = null;
this.old_settings_data = null;
config.need_render = true;
}
free() {
this.settings_reference = null;
this.old_settings_data = null;
this.mirror_selection_settings = null;
}
}
@@ -0,0 +1,57 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Select_layer_action extends Base_action {
/**
* marks layer as selected, active
*
* @param {int} layer_id
*/
constructor(layer_id, ignore_same_selection = false) {
super('select_layer', 'Select Layer');
this.reset_selection_action = null;
this.layer_id = parseInt(layer_id);
this.ignore_same_selection = ignore_same_selection;
this.old_layer = null;
}
async do() {
super.do();
let old_layer = config.layer;
let new_layer = app.Layers.get_layer(this.layer_id);
if (old_layer !== new_layer) {
this.old_layer = old_layer;
config.layer = new_layer;
} else if (!this.ignore_same_selection) {
throw new Error('Aborted - Layer already selected');
}
this.reset_selection_action = new app.Actions.Reset_selection_action();
await this.reset_selection_action.do();
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
if (this.reset_selection_action) {
await this.reset_selection_action.undo();
this.reset_selection_action = null;
}
config.layer = this.old_layer;
this.old_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
free() {
this.old_layer = null;
}
}
@@ -0,0 +1,33 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Select_next_layer_action extends Base_action {
constructor(reference_layer_id) {
super('select_next_layer', 'Select Next Layer');
this.reference_layer_id = reference_layer_id;
this.old_config_layer = null;
}
async do() {
super.do();
const next_layer = app.Layers.find_next(this.reference_layer_id);
if (!next_layer) {
throw new Error('Aborted - Next layer to select not found');
}
this.old_config_layer = config.layer;
config.layer = next_layer;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
config.layer = this.old_config_layer;
this.old_config_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
}
@@ -0,0 +1,33 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Select_previous_layer_action extends Base_action {
constructor(reference_layer_id) {
super('select_previous_layer', 'Select Previous Layer');
this.reference_layer_id = reference_layer_id;
this.old_config_layer = null;
}
async do() {
super.do();
const previous_layer = app.Layers.find_previous(this.reference_layer_id);
if (!previous_layer) {
throw new Error('Aborted - Previous layer to select not found');
}
this.old_config_layer = config.layer;
config.layer = previous_layer;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
config.layer = this.old_config_layer;
this.old_config_layer = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
}
@@ -0,0 +1,35 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Set_object_property_action extends Base_action {
/**
* Sets a generic object property. I recommend against using this as it's generally a hack for edge cases.
*
* @param {string} layer_id
* @param {object} settings
*/
constructor(object, property_name, value) {
super('set_object_property', 'Set Object Property');
this.object = object;
this.property_name = property_name;
this.value = value;
this.old_value = null;
}
async do() {
super.do();
this.old_value = this.object[this.property_name];
this.object[this.property_name] = this.value;
}
async undo() {
super.undo();
this.object[this.property_name] = this.old_value;
this.old_value = null;
}
free() {
this.object = null;
}
}
@@ -0,0 +1,57 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Set_selection_action extends Base_action {
/**
* Sets the selection to the specified position and dimensions
*/
constructor(x, y, width, height, old_settings_override) {
super('set_selection', 'Set Selection');
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.settings_reference = null;
this.old_settings_data = null;
this.old_settings_override = old_settings_override ? JSON.parse(JSON.stringify(old_settings_override)) || null : null;
}
async do() {
super.do();
this.settings_reference = app.Layers.Base_selection.find_settings();
this.old_settings_data = JSON.parse(JSON.stringify(this.settings_reference.data));
if (this.x != null)
this.settings_reference.data.x = this.x;
if (this.y != null)
this.settings_reference.data.y = this.y;
if (this.width != null)
this.settings_reference.data.width = this.width;
if (this.height != null)
this.settings_reference.data.height = this.height;
config.need_render = true;
}
async undo() {
super.undo()
if (this.old_settings_override) {
for (let prop in this.old_settings_override) {
this.settings_reference.data[prop] = this.old_settings_override[prop];
}
} else {
for (let prop in this.old_settings_data) {
this.settings_reference.data[prop] = this.old_settings_data[prop];
}
}
this.settings_reference = null;
this.old_settings_data = null;
config.need_render = true;
}
free() {
this.settings_reference = null;
this.old_settings_override = null;
this.old_settings_data = null;
}
}
@@ -0,0 +1,59 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Stop_animation_action extends Base_action {
/**
* Stops the currently playing animation, both do and undo states will stop animation
*/
constructor(reset_layer_visibility) {
super('stop_animation', 'Stop Animation');
this.reset_layer_visibility = !!reset_layer_visibility;
}
async do() {
super.do();
const animation_tool = app.GUI.GUI_tools.tools_modules.animation.object;
var params = animation_tool.getParams();
if (animation_tool.intervalID == null)
return;
clearInterval(animation_tool.intervalID);
params.play = false;
animation_tool.index = 0;
animation_tool.GUI_tools.show_action_attributes();
// make all visible
if (this.reset_layer_visibility) {
for (let i in config.layers) {
config.layers[i].visible = true;
}
}
animation_tool.Base_gui.GUI_layers.render_layers();
config.need_render = true;
}
async undo() {
super.undo();
const animation_tool = app.GUI.GUI_tools.tools_modules.animation.object;
var params = animation_tool.getParams();
if (animation_tool.intervalID == null)
return;
clearInterval(animation_tool.intervalID);
params.play = false;
animation_tool.index = 0;
animation_tool.GUI_tools.show_action_attributes();
// make all visible
if (this.reset_layer_visibility) {
for (let i in config.layers) {
config.layers[i].visible = true;
}
}
animation_tool.Base_gui.GUI_layers.render_layers();
config.need_render = true;
}
}
@@ -0,0 +1,222 @@
import { v4 as uuidv4 } from 'uuid';
// Get a unique id to identify this tab's history in the database
let tabUuid;
try {
tabUuid = sessionStorage.getItem('history_tab_uuid');
} catch (error) {}
if (!tabUuid) {
tabUuid = uuidv4();
try {
sessionStorage.setItem('history_tab_uuid', tabUuid);
} catch (error) {}
}
let imageIdCounter = 0;
let database = null;
let databaseInitPromise = null;
const tabPingInterval = 60000;
const assumeTabIsClosedTimeout = 300000; // Inactive tabs setInterval is slowed down in most browsers, this should be significantly higher than tabPingInterval
export default {
/**
* Initializes the database
*/
async init() {
if (!databaseInitPromise) {
databaseInitPromise = new Promise(async (resolveInit) => {
try {
if (window.indexedDB) {
// Delete database from a previous page load, if no other tabs have notified that they're open in a while
let shouldDeleteDatabase = true;
try {
let lastDatabaseTabPing = localStorage.getItem('history_usage_ping');
shouldDeleteDatabase = (!lastDatabaseTabPing || parseInt(lastDatabaseTabPing, 10) < new Date().getTime() - assumeTabIsClosedTimeout);
} catch (error) {}
if (shouldDeleteDatabase) {
await new Promise((resolve, reject) => {
let deleteRequest = window.indexedDB.deleteDatabase('undoHistoryImageStore');
deleteRequest.onerror = () => {
reject(deleteRequest.error);
};
deleteRequest.onsuccess = () => {
resolve();
};
});
}
// Initialize database
await new Promise((resolve, reject) => {
let openRequest = window.indexedDB.open('undoHistoryImageStore', 1);
openRequest.onupgradeneeded = function(event) {
database = openRequest.result;
switch (event.oldVersion) {
case 0:
database.createObjectStore('images', { keyPath: 'id' });
break;
}
};
openRequest.onerror = () => {
reject(openRequest.error);
}
openRequest.onsuccess = () => {
resolve();
database = openRequest.result;
}
});
if (!database) {
throw new Error('indexedDB not initialized');
}
// Delete history from previous session
try {
await this.delete_all();
} catch (error) {}
// Ping localStorage for as long as this browser tab is open
localStorage.setItem('history_usage_ping', new Date().getTime() + '');
setInterval(() => {
localStorage.setItem('history_usage_ping', new Date().getTime() + '');
}, tabPingInterval);
}
} catch (error) {
database = {
isMemory: true,
images: {}
};
}
resolveInit();
});
await databaseInitPromise;
} else if (!database) {
await databaseInitPromise;
}
},
/**
* Adds the specified image to the database. Returns a promise that is resolved with an id that can be used to retrieve it again.
*
* @param {string | canvas | ImageData} imageData the image data to store
* @returns {Promise<string>} resolves with retrieval id
*/
async add(imageData) {
await this.init();
let imageId = tabUuid + '-' + (imageIdCounter++);
if (database.isMemory) {
database.images[imageId] = imageData;
} else {
await new Promise((resolve, reject) => {
const transaction = database.transaction('images', 'readwrite');
const images = transaction.objectStore('images');
const image = {
id: imageId,
tabUuid,
data: imageData
}
const request = images.add(image);
request.onsuccess = function() {
resolve();
};
request.onerror = function() {
reject(request.error);
};
});
}
return imageId;
},
/**
* Gets the specified image from the database, by imageId retrieved from "add()" method.
*
* @param {string} imageId the id of the image to get
* @returns {Promise<string | canvas | ImageData>} resolves with the image
*/
async get(imageId) {
await this.init();
if (database.isMemory) {
return database.images[imageId];
} else {
return new Promise((resolve, reject) => {
const transaction = database.transaction('images', 'readonly');
const images = transaction.objectStore('images');
const request = images.get(imageId);
request.onsuccess = function() {
resolve(request.result && request.result.data);
};
request.onerror = function() {
reject(request.error);
};
});
}
},
/**
* Deletes the specified image from the database, by imageId retrieved from "add()" method.
*
* @param {string} imageId the id of the image to delete
* @returns {Promise<void>}
*/
async delete(imageId) {
await this.init();
if (database.isMemory) {
delete database.images[imageId];
} else {
return new Promise((resolve, reject) => {
const transaction = database.transaction('images', 'readwrite');
const images = transaction.objectStore('images');
const request = images.delete(imageId);
request.onsuccess = function() {
resolve();
};
request.onerror = function() {
reject(request.error);
};
});
}
},
/**
* Deletes all images associated with the current tab.
*
* @returns {Promise<void>}
*/
async delete_all() {
await this.init();
if (database.isMemory) {
database.images = {};
} else {
return new Promise((resolve, reject) => {
const transaction = database.transaction('images', 'readwrite');
const images = transaction.objectStore('images');
const getAllImagesRequest = images.getAll();
getAllImagesRequest.onsuccess = async function () {
const allImages = getAllImagesRequest.result;
let errorOccurred = false;
for (let image of allImages) {
if (image.tabUuid === tabUuid) {
try {
await new Promise((deleteResolve, deleteReject) => {
const request = images.delete(image.id);
request.onsuccess = function() {
deleteResolve();
};
request.onerror = function() {
deleteReject(request.error);
};
});
} catch (error) {
errorOccurred = true;
// Should eventually be deleted when database is deleted due to timeout
}
}
}
if (errorOccurred) {
// Use a different uuid to prevent conflicts
tabUuid = uuidv4();
}
resolve();
};
getAllImagesRequest.onerror = function () {
reject(request.error);
};
});
}
}
};
@@ -0,0 +1,37 @@
import app from '../app.js';
import config from '../config.js';
import { Base_action } from './base.js';
export class Toggle_layer_visibility_action extends Base_action {
/**
* toggle layer visibility
*
* @param {int} layer_id
*/
constructor(layer_id) {
super('toggle_layer_visibility', 'Toggle Layer Visibility');
this.layer_id = parseInt(layer_id);
this.old_visible = null;
}
async do() {
super.do();
const layer = app.Layers.get_layer(this.layer_id);
this.old_visible = layer.visible;
if (layer.visible == false)
layer.visible = true;
else
layer.visible = false;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
async undo() {
super.undo();
const layer = app.Layers.get_layer(this.layer_id);
layer.visible = this.old_visible;
this.old_visible = null;
app.Layers.render();
app.GUI.GUI_layers.render_layers();
}
}
@@ -0,0 +1,37 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Update_config_action extends Base_action {
/**
* Updates the app config with the provided settings
*
* @param {object} settings
*/
constructor(settings) {
super('update_config', 'Update Config');
this.settings = settings;
this.old_settings = {};
}
async do() {
super.do();
for (let i in this.settings) {
this.old_settings[i] = config[i];
config[i] = this.settings[i];
}
}
async undo() {
super.undo();
for (let i in this.old_settings) {
config[i] = this.old_settings[i];
}
this.old_settings = {};
}
free() {
this.settings = null;
this.old_settings = null;
}
}
@@ -0,0 +1,149 @@
import app from './../app.js';
import config from './../config.js';
import Helper_class from './../libs/helpers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
import image_store from './store/image-store.js';
import { Base_action } from './base.js';
const Helper = new Helper_class();
export class Update_layer_image_action extends Base_action {
/**
* updates layer image data
*
* @param {canvas} canvas
* @param {int} layer_id (optional)
*/
constructor(canvas, layer_id) {
super('update_layer_image', 'Update Layer Image');
this.canvas = canvas;
if (layer_id == null)
layer_id = config.layer.id;
this.layer_id = parseInt(layer_id);
this.reference_layer = null;
this.old_image_id = null;
this.new_image_id = null;
this.old_link_database_id = null;
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
if (this.reference_layer.type != 'image'){
alertify.error('Error: layer must be image.');
throw new Error('Aborted - layer is not an image');
}
// Get data url representation of image
let canvas_data_url;
if (this.new_image_id) {
try {
canvas_data_url = await image_store.get(this.new_image_id);
} catch (error) {
throw new Error('Aborted - problem retrieving cached image from database');
}
} else if (this.canvas) {
if (Helper.is_edge_or_ie() == false && typeof(FileReader) !== 'undefined') {
// Update image using blob and FileReader (async)
await new Promise((resolve) => {
this.canvas.toBlob((blob) => {
var reader = new FileReader();
reader.onloadend = () => {
canvas_data_url = reader.result;
resolve();
}
reader.readAsDataURL(blob);
}, 'image/png');
});
}
else {
// Slow way for IE, Edge
canvas_data_url = this.canvas.toDataURL();
}
}
// Store data url in database
try {
if (!this.old_image_id) {
if (this.reference_layer._link_database_id) {
this.old_image_id = this.reference_layer._link_database_id;
} else {
this.old_image_id = await image_store.add(this.reference_layer.link.src);
}
}
if (!this.new_image_id) {
this.new_image_id = await image_store.add(canvas_data_url);
}
} catch (error) {
console.log(error);
requestAnimationFrame(() => {
app.State.free(0, this.database_estimate || 1)
});
}
// Estimate storage size
try {
this.database_estimate = new Blob([await image_store.get(this.old_image_id)]).size;
} catch (e) {}
// Assign layer properties
this.reference_layer.link.src = canvas_data_url;
this.old_link_database_id = this.reference_layer._link_database_id;
this.reference_layer._link_database_id = this.new_image_id;
this.canvas = null;
config.need_render = true;
}
async undo() {
super.undo();
// Estimate storage size
try {
this.database_estimate = new Blob([this.reference_layer.link.src]).size;
} catch (e) {}
// Restore old image
if (this.old_image_id != null) {
try {
this.reference_layer.link.src = await image_store.get(this.old_image_id);
} catch (error) {
throw new Error('Failed to retrieve image from store');
}
}
this.reference_layer._link_database_id = this.old_link_database_id;
this.reference_layer = null;
config.need_render = true;
}
async free() {
let has_error = false;
if (this.new_image_id != null) {
try {
await image_store.delete(this.new_image_id);
} catch (error) {
has_error = true;
}
this.new_image_id = null;
}
if (this.is_done || !this.old_link_database_id) {
if (this.old_image_id != null) {
try {
await image_store.delete(this.old_image_id);
} catch (error) {
has_error = true;
}
this.old_image_id = null;
}
}
this.canvas = null;
this.old_link_database_id = null;
this.reference_layer = null;
if (has_error) {
alertify.error('A problem occurred while removing undo history. It\'s suggested you save your work and refresh the page in order to free up memory.');
}
}
}
@@ -0,0 +1,67 @@
import app from './../app.js';
import config from './../config.js';
import { Base_action } from './base.js';
export class Update_layer_action extends Base_action {
/**
* Updates an existing layer with the provided settings
* WARNING: If passing objects or arrays into settings, make sure these are new or cloned objects, and not a modified existing object!
*
* @param {string} layer_id
* @param {object} settings
*/
constructor(layer_id, settings) {
super('update_layer', 'Update Layer');
this.layer_id = layer_id;
this.settings = settings;
this.reference_layer = null;
this.old_settings = {};
}
async do() {
super.do();
this.reference_layer = app.Layers.get_layer(this.layer_id);
if (!this.reference_layer) {
throw new Error('Aborted - layer with specified id doesn\'t exist');
}
for (let i in this.settings) {
if (i == 'id')
continue;
if (i == 'order')
continue;
this.old_settings[i] = this.reference_layer[i];
this.reference_layer[i] = this.settings[i];
}
if (this.reference_layer.type === 'text') {
this.reference_layer._needs_update_data = true;
}
if (this.settings.params || this.settings.width || this.settings.height) {
config.need_render_changed_params = true;
}
config.need_render = true;
}
async undo() {
super.undo();
if (this.reference_layer) {
for (let i in this.old_settings) {
this.reference_layer[i] = this.old_settings[i];
}
if (this.reference_layer.type === 'text') {
this.reference_layer._needs_update_data = true;
}
if (this.old_settings.params || this.old_settings.width || this.old_settings.height) {
config.need_render_changed_params = true;
}
this.old_settings = {};
}
this.reference_layer = null;
config.need_render = true;
}
free() {
this.settings = null;
this.old_settings = null;
this.reference_layer = null;
}
}
@@ -0,0 +1,84 @@
/**
* Backend capabilities singleton.
* Fetched once on load from GET /api/config.
* Tools use this to decide whether to show, grey out, or show tooltips.
*
* Shape:
* {
* local: { lama, rembg, opencv, gpu_detected },
* remote: { provider, capabilities: string[], healthy }
* }
*/
import apiService from '../services/api.js';
const DEFAULT_CAPS = {
local: { lama: false, rembg: false, opencv: true, gpu_detected: false },
remote: { provider: null, capabilities: [], healthy: false },
};
let _caps = null;
let _fetchPromise = null;
let _gpuStatus = null;
let _gpuFetchPromise = null;
/**
* Return capabilities (fetched lazily, cached thereafter).
* Always resolves — falls back to DEFAULT_CAPS on network error.
*/
export async function getCapabilities() {
if (_caps) return _caps;
if (!_fetchPromise) {
_fetchPromise = apiService.getConfig()
.then(data => { _caps = data || DEFAULT_CAPS; return _caps; })
.catch(() => { _caps = DEFAULT_CAPS; return _caps; });
}
return _fetchPromise;
}
/**
* Synchronous check — returns cached value or DEFAULT_CAPS if not yet loaded.
*/
export function getCachedCapabilities() {
return _caps || DEFAULT_CAPS;
}
/**
* True if the remote provider is configured and healthy.
*/
export function hasRemote() {
return !!(_caps?.remote?.healthy);
}
/**
* Fetch and cache detailed GPU status (hardware, feature flags, model selection per op).
* Calls /api/gpu/status — only meaningful when AI_PROVIDER=local_gpu.
* Returns null on error.
*/
export async function getGpuStatus() {
if (_gpuStatus !== null) return _gpuStatus;
if (!_gpuFetchPromise) {
_gpuFetchPromise = apiService.getGpuStatus()
.then(data => { _gpuStatus = data; return _gpuStatus; })
.catch(() => { _gpuStatus = null; return null; });
}
return _gpuFetchPromise;
}
/**
* Invalidate cache and re-fetch (call after saving provider settings).
*/
export async function refreshCapabilities() {
_caps = null;
_fetchPromise = null;
_gpuStatus = null;
_gpuFetchPromise = null;
return getCapabilities();
}
/**
* Kick off the fetch immediately at module load time so it's ready when tools need it.
*/
getCapabilities();
export default { getCapabilities, getCachedCapabilities, hasRemote, refreshCapabilities, getGpuStatus };
+11
View File
@@ -0,0 +1,11 @@
// Store singletons for easy access
export default {
GUI: null,
Tools: null,
Layers: null,
Config: null,
State: null,
FileOpen: null,
FileSave: null,
Actions: null
};
+916
View File
@@ -0,0 +1,916 @@
const menuDefinition = [
{
name: 'File',
children: [
{
name: 'New',
target: 'file/new.new'
},
{
divider: true
},
{
name: 'Open',
children: [
{
name: 'Open File',
shortcut: 'O',
ellipsis: true,
target: 'file/open.open_file'
},
{
name: 'Open Directory',
ellipsis: true,
target: 'file/open.open_dir'
},
{
name: 'Open from Webcam',
target: 'file/open.open_webcam'
},
{
name: 'Open URL',
ellipsis: true,
target: 'file/open.open_url'
},
{
name: 'Open Data URL',
ellipsis: true,
target: 'file/open.open_data_url'
},
{
name: 'Open Test Template',
target: 'file/open.open_template_test'
}
]
},
{
name: 'Search Images',
ellipsis: true,
target: 'file/open.search'
},
{
divider: true
},
{
name: 'Export',
ellipsis: true,
shortcut: 'S',
target: 'file/save.export'
},
{
name: 'Save As',
ellipsis: true,
shortcut: 'Shift + S',
target: 'file/save.save'
},
{
name: 'Save As Data URL',
ellipsis: true,
target: 'file/save.save_data_url'
},
{
name: 'Print',
ellipsis: true,
shortcut: 'Ctrl+P',
target: 'file/print.print'
},
{
divider: true
},
{
name: 'Quick Save',
shortcut: 'F9',
target: 'file/quicksave.quicksave'
},
{
name: 'Quick Load',
shortcut: 'F10',
target: 'file/quickload.quickload'
},
{
divider: true
},
{
name: 'My Library',
children: [
{
name: 'Browse Library',
ellipsis: true,
target: 'file/my_library.browse_library'
},
{
divider: true
},
{
name: 'Save Layer to Library',
ellipsis: true,
target: 'file/my_library.save_to_library'
},
{
name: 'Save Selection to Library',
ellipsis: true,
target: 'file/my_library.save_selection_to_library'
},
{
divider: true
},
{
name: 'Export Library (Backup)',
target: 'file/my_library.export_library'
},
{
name: 'Import Library',
ellipsis: true,
target: 'file/my_library.import_library'
}
]
}
]
},
{
name: 'Edit',
children: [
{
name: 'Undo',
shortcut: 'Ctrl+Z',
target: 'edit/undo.undo'
},
{
name: 'Redo',
shortcut: 'Ctrl+Y',
target: 'edit/redo.redo'
},
{
name: 'History Panel',
shortcut: 'Ctrl+H',
target: 'edit/history_panel.toggle'
},
{
divider: true
},
{
name: 'Delete Selection',
shortcut: 'Del',
target: 'edit/selection.delete'
},
{
name: 'Copy Selection',
target: 'layer/new.new_selection'
},
{
name: 'Copy to Clipboard',
shortcut: 'Ctrl+C',
target: 'edit/copy.copy_to_clipboard'
},
{
name: 'Paste',
shortcut: 'Ctrl+V',
target: 'edit/paste.paste'
},
{
divider: true
},
{
name: 'Select All',
shortcut: 'Ctrl+A',
target: 'edit/selection.select_all'
}
]
},
{
name: 'View',
children: [
{
name: 'Zoom',
children: [
{
name: 'Zoom In',
target: 'view/zoom.in'
},
{
name: 'Zoom Out',
target: 'view/zoom.out'
},
{
divider: true
},
{
name: 'Original Size',
target: 'view/zoom.original'
},
{
name: 'Fit Window',
target: 'view/zoom.auto'
}
]
},
{
name: 'Grid',
shortcut: 'G',
target: 'view/grid.grid'
},
{
name: 'Guides',
children: [
{
name: 'Insert',
ellipsis: true,
target: 'view/guides.insert'
},
{
name: 'Update',
target: 'view/guides.update'
},
{
name: 'Remove all',
target: 'view/guides.remove'
}
]
},
{
name: 'Ruler',
target: 'view/ruler.ruler'
},
{
divider: true
},
{
name: 'Full Screen',
target: 'view/full_screen.fs'
}
]
},
{
name: 'Image',
children: [
{
name: 'Information',
shortcut: 'I',
ellipsis: true,
target: 'image/information.information'
},
{
name: 'Canvas Size',
ellipsis: true,
target: 'image/size.size'
},
{
name: 'Trim',
ellipsis: true,
shortcut: 'T',
target: 'image/trim.trim'
},
{
divider: true
},
{
name: 'Resize',
ellipsis: true,
shortcut: 'R',
target: 'image/resize.resize'
},
{
name: 'Rotate',
ellipsis: true,
target: 'image/rotate.rotate'
},
{
name: 'Flip',
children: [
{
name: 'Vertical',
target: 'image/flip.vertical'
},
{
name: 'Horizontal',
target: 'image/flip.horizontal'
}
]
},
{
name: 'Translate',
ellipsis: true,
target: 'image/translate.translate'
},
{
name: 'Opacity',
ellipsis: true,
target: 'image/opacity.opacity'
},
{
divider: true
},
{
name: 'Color Corrections',
ellipsis: true,
target: 'image/color_corrections.color_corrections'
},
{
name: 'Auto Adjust Colors',
shortcut: 'F',
target: 'image/auto_adjust.auto_adjust'
},
{
name: 'Decrease Color Depth',
target: 'image/decrease_colors.decrease_colors'
},
{
name: 'Color Palette',
ellipsis: true,
target: 'image/palette.palette'
},
{
divider: true
},
{
name: 'Histogram',
ellipsis: true,
target: 'image/histogram.histogram'
},
{
divider: true
},
{
name: 'Auto-Enhance',
ellipsis: true,
target: 'image/auto_enhance.auto_enhance'
},
{
name: 'Extract Color Palette',
target: 'image/color_palette.color_palette'
},
{
name: 'Remove Background (AI)',
ellipsis: true,
target: 'image/remove_background.remove_background'
},
{
name: 'Replace Subject (AI)...',
ellipsis: true,
target: 'image/replace_subject.replace_subject'
},
{
name: 'Prepare for Print...',
ellipsis: true,
target: 'image/print_prepare.print_prepare'
},
{
name: 'Fit to Frame...',
ellipsis: true,
target: 'image/frame_fit.frame_fit'
},
{
name: 'Upscale...',
ellipsis: true,
target: 'image/upscale.upscale'
},
{
divider: true
},
{
name: 'Selection Effects',
children: [
{
name: 'Invert Selection',
ellipsis: true,
target: 'image/selection_effects.invert_selection'
},
{
name: 'Adjust Selection',
ellipsis: true,
target: 'image/selection_effects.adjust_selection'
},
{
name: 'Greyscale Selection',
ellipsis: true,
target: 'image/selection_effects.greyscale_selection'
}
]
}
]
},
{
name: 'Layer',
children: [
{
name: 'New',
shortcut: 'N',
target: 'layer/new.new'
},
{
name: 'New from Selection',
target: 'layer/new.new_selection'
},
{
divider: true
},
{
name: 'Duplicate',
shortcut: 'D',
target: 'layer/duplicate.duplicate'
},
{
name: 'Show / Hide',
target: 'layer/visibility.toggle'
},
{
name: 'Delete',
target: 'layer/delete.delete'
},
{
name: 'Convert to Raster',
target: 'layer/raster.raster'
},
{
name: 'Scale Layer',
ellipsis: true,
target: 'layer/scale.scale'
},
{
name: 'Align to Canvas',
target: 'layer/align.align'
},
{
divider: true
},
{
name: 'Move',
children: [
{
name: 'Up',
target: 'layer/move.up'
},
{
name: 'Down',
target: 'layer/move.down'
}
]
},
{
name: 'Composition',
ellipsis: true,
target: 'layer/composition.composition'
},
{
name: 'Rename',
ellipsis: true,
target: 'layer/rename.rename'
},
{
name: 'Clear',
target: 'layer/clear.clear'
},
{
divider: true
},
{
name: 'Differences Down',
target: 'layer/differences.differences'
},
{
name: 'Merge Down',
target: 'layer/merge.merge'
},
{
name: 'Flatten Image',
target: 'layer/flatten.flatten'
}
]
},
{
name: 'Effects',
children: [
{
name: 'Effect browser',
ellipsis: true,
target: 'effects/browser.browser'
},
{
divider: true
},
{
name: 'Common Filters',
children: [
{
name: 'Gaussian Blur',
ellipsis: true,
target: 'effects/common/blur.blur'
},
{
name: 'Brightness',
ellipsis: true,
target: 'effects/common/brightness.brightness'
},
{
name: 'Contrast',
ellipsis: true,
target: 'effects/common/contrast.contrast'
},
{
name: 'Grayscale',
ellipsis: true,
target: 'effects/common/grayscale.grayscale'
},
{
name: 'Hue Rotate',
ellipsis: true,
target: 'effects/common/hue-rotate.hue_rotate'
},
{
name: 'Negative',
ellipsis: true,
target: 'effects/common/invert.invert'
},
{
name: 'Saturate',
ellipsis: true,
target: 'effects/common/saturate.saturate'
},
{
name: 'Sepia',
ellipsis: true,
target: 'effects/common/sepia.sepia'
},
{
name: 'Shadow',
ellipsis: true,
target: 'effects/common/shadow.shadow'
},
]
},
{
name: 'Instagram Filters',
children: [
{
name: '1977',
target: 'effects/instagram/1977.1977'
},
{
name: 'Aden',
target: 'effects/instagram/aden.aden'
},
{
name: 'Clarendon',
target: 'effects/instagram/clarendon.clarendon'
},
{
name: 'Gingham',
target: 'effects/instagram/gingham.gingham'
},
{
name: 'Inkwell',
target: 'effects/instagram/inkwell.inkwell'
},
{
name: 'Lo-fi',
target: 'effects/instagram/lofi.lofi'
},
{
name: 'Toaster',
target: 'effects/instagram/toaster.toaster'
},
{
name: 'Valencia',
target: 'effects/instagram/valencia.valencia'
},
{
name: 'X-Pro II',
target: 'effects/instagram/xpro2.xpro2'
}
]
},
{
name: 'Black and White',
ellipsis: true,
target: 'effects/black_and_white.black_and_white'
},
{
name: 'Greyscale',
ellipsis: true,
target: 'effects/greyscale.greyscale'
},
{
name: 'Borders',
ellipsis: true,
target: 'effects/borders.borders'
},
{
name: 'Blueprint',
target: 'effects/blueprint.blueprint'
},
{
name: 'Box Blur',
ellipsis: true,
target: 'effects/box_blur.box_blur'
},
{
name: 'Denoise',
ellipsis: true,
target: 'effects/denoise.denoise'
},
{
name: 'Dither',
ellipsis: true,
target: 'effects/dither.dither'
},
{
name: 'Dot Screen',
ellipsis: true,
target: 'effects/dot_screen.dot_screen'
},
{
name: 'Edge',
target: 'effects/edge.edge'
},
{
name: 'Emboss',
target: 'effects/emboss.emboss'
},
{
name: 'Enrich',
ellipsis: true,
target: 'effects/enrich.enrich'
},
{
name: 'Grains',
ellipsis: true,
target: 'effects/grains.grains'
},
{
name: 'Heatmap',
target: 'effects/heatmap.heatmap'
},
{
name: 'Mosaic',
ellipsis: true,
target: 'effects/mosaic.mosaic'
},
{
name: 'Night Vision',
target: 'effects/night_vision.night_vision'
},
{
name: 'Oil',
ellipsis: true,
target: 'effects/oil.oil'
},
{
name: 'Pencil',
target: 'effects/pencil.pencil'
},
{
name: 'Sharpen',
ellipsis: true,
target: 'effects/sharpen.sharpen'
},
{
name: 'Solarize',
target: 'effects/solarize.solarize'
},
{
name: 'Tilt Shift',
ellipsis: true,
target: 'effects/tilt_shift.tilt_shift'
},
{
name: 'Vignette',
ellipsis: true,
target: 'effects/vignette.vignette'
},
{
name: 'Vibrance',
ellipsis: true,
target: 'effects/vibrance.vibrance'
},
{
name: 'Vintage',
ellipsis: true,
target: 'effects/vintage.vintage'
},
{
name: 'Zoom Blur',
ellipsis: true,
target: 'effects/zoom_blur.zoom_blur'
}
]
},
{
name: 'Tools',
children: [
{
name: 'Sprites',
target: 'tools/sprites.sprites'
},
{
name: 'Key-Points',
target: 'tools/keypoints.keypoints'
},
{
name: 'Content Fill',
ellipsis: true,
target: 'tools/content_fill.content_fill'
},
{
divider: true
},
{
name: 'Color Zoom',
ellipsis: true,
target: 'tools/color_zoom.color_zoom'
},
{
name: 'Replace Color',
ellipsis: true,
target: 'tools/replace_color.replace_color'
},
{
name: 'Restore Alpha',
ellipsis: true,
target: 'tools/restore_alpha.restore_alpha'
},
{
name: 'External',
children: [
{
name: 'TINYPNG - Compress PNG and JPEG',
href: 'https://tinypng.com'
},
{
name: 'REMOVE.BG - Remove Image Background',
href: 'https://www.remove.bg'
},
{
name: 'PNGTOSVG - Convert Image to SVG',
href: 'https://www.pngtosvg.com'
},
{
name: 'SQUOOSH - Compress and Compare Images',
href: 'https://squoosh.app'
}
]
},
{
divider: true
},
{
name: 'Language',
children: [
{
name: 'English',
target: 'tools/translate.translate',
parameter: 'en',
},
{
divider: true
},
{
//Arabic
name: 'عربي',
target: 'tools/translate.translate',
parameter: 'ar',
},
{
//Chinese simplified
name: '简体中文',
target: 'tools/translate.translate',
parameter: 'zh',
},
{
name: 'Deutsch',
target: 'tools/translate.translate',
parameter: 'de',
},
{
name: 'Dutch',
target: 'tools/translate.translate',
parameter: 'nl',
},
{
name: 'English (UK)',
target: 'tools/translate.translate',
parameter: 'uk',
},
{
name: 'Español',
target: 'tools/translate.translate',
parameter: 'es',
},
{
name: 'Français',
target: 'tools/translate.translate',
parameter: 'fr',
},
{
name: 'Greek',
target: 'tools/translate.translate',
parameter: 'el',
},
{
name: 'Italiano',
target: 'tools/translate.translate',
parameter: 'it',
},
{
//Japanese
name: '日本語',
target: 'tools/translate.translate',
parameter: 'ja',
},
{
//Korean
name: '한국어',
target: 'tools/translate.translate',
parameter: 'ko',
},
{
name: 'Lietuvių',
target: 'tools/translate.translate',
parameter: 'lt',
},
{
name: 'Português',
target: 'tools/translate.translate',
parameter: 'pt',
},
{
name: 'русский язык',
target: 'tools/translate.translate',
parameter: 'ru',
},
{
name: 'Türkçe',
target: 'tools/translate.translate',
parameter: 'tr',
}
]
},
{
name: 'Search',
shortcut: 'F3',
ellipsis: true,
target: 'tools/search.search'
},
{
name: 'Settings',
ellipsis: true,
target: 'tools/settings.settings'
},
{
divider: true
},
{
name: 'AI Provider Settings',
ellipsis: true,
target: 'tools/ai_provider_settings.ai_provider_settings'
}
]
},
{
name: 'Generate',
children: [
{
name: 'Add Text',
ellipsis: true,
target: 'text/text_presets.add_preset'
},
{
divider: true
},
{
name: 'Text → Image',
ellipsis: true,
target: 'generate/text_to_image.text_to_image'
},
{
name: 'Expand Canvas (Outpaint)',
ellipsis: true,
target: 'generate/outpaint.outpaint'
},
]
},
{
name: 'Help',
children: [
{
name: 'Keyboard Shortcuts',
ellipsis: true,
target: 'help/shortcuts.shortcuts'
},
{
name: 'Report Issues',
href: 'https://github.com/viliusle/miniPaint/issues'
},
{
divider: true
},
{
name: 'About',
ellipsis: true,
target: 'help/about.about'
}
]
}
];
export default menuDefinition;
+559
View File
@@ -0,0 +1,559 @@
//main config file
var config = {};
config.TRANSPARENCY = false;
config.TRANSPARENCY_TYPE = 'squares'; //squares, green, grey
config.LANG = 'en';
config.WIDTH = null;
config.HEIGHT = null;
config.visible_width = null;
config.visible_height = null;
config.COLOR = '#008000';
config.ALPHA = 255;
config.ZOOM = 1;
config.SNAP = true;
config.pixabay_key = '3ca2cd8af3fde33af218bea02-9021417';
config.safe_search_can_be_disabled = true;
config.google_webfonts_key = 'AIzaSyAC_Tx8RKkvN235fXCUyi_5XhSaRCzNhMg';
config.layers = [];
config.layer = null;
config.need_render = false;
config.need_render_changed_params = false; // Set specifically when param change in layer details triggered render
config.mouse = {};
config.mouse_lock = null;
config.swatches = {
default: [] // Only default used right now, object format for swatch swapping in future.
};
config.user_fonts = {};
config.guides_enabled = true;
config.guides = [];
config.ruler_active = false;
config.enable_autoresize_by_default = true;
//requires styles in reset.css
config.themes = [
'dark',
'light',
'green',
];
//no-translate BEGIN
config.FONTS = [
"Arial",
"Courier",
"Impact",
"Helvetica",
"Monospace",
"Tahoma",
"Times New Roman",
"Verdana",
"Amatic SC",
"Arimo",
"Codystar",
"Creepster",
"Indie Flower",
"Lato",
"Lora",
"Merriweather",
"Monoton",
"Montserrat",
"Mukta",
"Muli",
"Nosifer",
"Nunito",
"Oswald",
"Orbitron",
"Pacifico",
"PT Sans",
"PT Serif",
"Playfair Display",
"Poppins",
"Raleway",
"Roboto",
"Rubik",
"Special Elite",
"Tangerine",
"Titillium Web",
"Ubuntu"
];
//no-translate END
config.TOOLS = [
{
name: 'select',
title: 'Select object tool',
on_activate: 'on_activate',
attributes: {
auto_select: true,
keep_ratio: true,
},
},
{
name: 'selection',
attributes: {},
on_leave: 'on_leave',
},
{
name: 'smart_select',
title: 'Smart Select (AI) - Click to select',
attributes: {},
},
{
name: 'brush_select',
title: 'Brush Select (AI) - Paint over to select',
attributes: {},
},
{
name: 'ai_edit',
title: 'AI Edit — paint mask, then Erase / Replace / Upscale / Expand',
on_activate: 'on_activate',
attributes: {
size: {
value: 30,
min: 5,
max: 200,
},
},
},
{
name: 'magic_wand',
title: 'Magic Wand (Color Select)',
attributes: {
tolerance: {
value: 30,
min: 0,
max: 100,
},
contiguous: true,
},
},
{
name: 'lasso',
title: 'Lasso (Freehand Select)',
attributes: {},
},
{
name: 'ellipse_select',
title: 'Ellipse Selection',
attributes: {},
},
{
name: 'brush',
attributes: {
size: 4,
pressure: false,
},
},
{
name: 'pencil',
attributes: {
size: 1,
pressure: false,
},
},
{
name: 'pick_color',
attributes: {
global: false,
},
},
{
name: 'erase',
on_update: 'on_params_update',
attributes: {
size: 30,
circle: true,
strict: true,
},
},
{
name: 'magic_erase',
title: 'Magic Eraser Tool',
attributes: {
power: 15,
anti_aliasing: true,
contiguous: false,
},
},
{
name: 'fill',
attributes: {
power: 5,
anti_aliasing: false,
contiguous: false,
},
},
{
name: 'shape',
on_activate: 'on_activate',
title: 'Shapes (H)',
attributes: {
size: 3,
stroke: '#00aa00',
},
},
{
name: 'line',
visible: false,
attributes: {
size: 4,
},
},
{
name: 'arrow',
visible: false,
attributes: {
size: 4,
},
},
{
name: 'rectangle',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
radius: {
value: 0,
min: 0,
},
square: false,
},
},
{
name: 'ellipse',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
circle: false,
},
},
{
name: 'media',
title: 'Search Images',
on_activate: 'on_activate',
attributes: {
size: 30,
},
},
{
name: 'triangle',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'right_triangle',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'romb',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'parallelogram',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'trapezoid',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'plus',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'pentagon',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'hexagon',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'star',
visible: false,
attributes: {
border_size: 4,
corners: 5,
inner_radius: 40,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'heart',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'cylinder',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'human',
visible: false,
attributes: {
border_size: 4,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'tear',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'cog',
visible: false,
attributes: {
fill_color: '#555555',
},
},
{
name: 'bezier_curve',
visible: false,
attributes: {
size: 4,
},
},
{
name: 'moon',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'callout',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
{
name: 'text',
on_update: 'on_params_update',
attributes: {
font: {
value: 'Arial',
values() {
const user_font_names = Object.keys(config.user_fonts);
return ['', '[Add Font...]', ...Array.from(new Set([...config.FONTS, ...user_font_names].sort()))];
}
},
size: 40,
bold: {
value: false,
icon: `bold.svg`
},
italic: {
value: false,
icon: `italic.svg`
},
underline: {
value: false,
icon: `underline.svg`
},
strikethrough: {
value: false,
icon: `strikethrough.svg`
},
fill: '#008800',
stroke: '#000000',
stroke_size: {
value: 0,
min: 0,
step: 0.1
},
kerning: {
value: 0,
min: -999,
max: 999,
step: 1
},
leading: {
value: 0,
min: -999,
max: 999,
step: 1
}
},
},
{
name: 'gradient',
attributes: {
color_1: '#008000',
color_2: '#ffffff',
alpha: 0,
radial: false,
radial_power: 50,
},
},
{
name: 'clone',
attributes: {
size: 30,
anti_aliasing: true,
source_layer: {
value: 'Current',
values: ['Current', 'Previous'],
},
},
},
{
name: 'crop',
on_update: 'on_params_update',
on_leave: 'on_leave',
attributes: {
crop: true,
},
},
{
name: 'blur',
attributes: {
size: 30,
strength: 1,
},
},
{
name: 'sharpen',
attributes: {
size: 30,
},
},
{
name: 'desaturate',
attributes: {
size: 50,
anti_aliasing: true,
},
},
{
name: 'bulge_pinch',
title: 'Bulge/Pinch Tool',
attributes: {
radius: 80,
power: 50,
bulge: true,
},
},
{
name: 'animation',
on_activate: 'on_activate',
on_update: 'on_params_update',
on_leave: 'on_leave',
attributes: {
play: false,
delay: 400,
},
},
{
name: 'polygon',
visible: false,
attributes: {
border_size: 4,
border: true,
fill: true,
border_color: '#555555',
fill_color: '#aaaaaa',
},
},
];
//link to active tool
config.TOOL = config.TOOLS[2];
export default config;
+522
View File
@@ -0,0 +1,522 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../config.js';
import Base_layers_class from './base-layers.js';
import GUI_tools_class from './gui/gui-tools.js';
import GUI_preview_class from './gui/gui-preview.js';
import GUI_colors_class from './gui/gui-colors.js';
import GUI_layers_class from './gui/gui-layers.js';
import GUI_information_class from './gui/gui-information.js';
import GUI_details_class from './gui/gui-details.js';
import GUI_menu_class from './gui/gui-menu.js';
import Tools_translate_class from './../modules/tools/translate.js';
import Tools_settings_class from './../modules/tools/settings.js';
import Helper_class from './../libs/helpers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
/**
* Main GUI class
*/
class Base_gui_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Helper = new Helper_class();
this.Base_layers = new Base_layers_class();
//last used menu id
this.last_menu = '';
//grid dimensions config
this.grid_size = [50, 50];
//if grid is visible
this.grid = false;
this.canvas_offset = {x: 0, y: 0};
//common image dimensions
this.common_dimensions = [
[640, 480, '480p'],
[800, 600, 'SVGA'],
[1024, 768, 'XGA'],
[1280, 720, 'hdtv, 720p'],
[1600, 1200, 'UXGA'],
[1920, 1080, 'Full HD, 1080p'],
[3840, 2160, '4K UHD'],
//[7680,4320, '8K UHD'],
];
this.GUI_tools = new GUI_tools_class(this);
this.GUI_preview = new GUI_preview_class(this);
this.GUI_colors = new GUI_colors_class(this);
this.GUI_layers = new GUI_layers_class(this);
this.GUI_information = new GUI_information_class(this);
this.GUI_details = new GUI_details_class(this);
this.GUI_menu = new GUI_menu_class();
this.Tools_translate = new Tools_translate_class();
this.Tools_settings = new Tools_settings_class();
this.modules = {};
}
init() {
this.load_modules();
this.load_default_values();
this.render_main_gui();
this.init_service_worker();
}
load_modules() {
var _this = this;
var modules_context = require.context("./../modules/", true, /\.js$/);
modules_context.keys().forEach(function (key) {
if (key.indexOf('Base' + '/') < 0) {
var moduleKey = key.replace('./', '').replace('.js', '');
try {
var classObj = modules_context(key);
_this.modules[moduleKey] = new classObj.default();
} catch (e) {
console.error('[load_modules] Failed to load ' + key + ':', e);
}
}
});
}
load_default_values() {
//transparency
var transparency_cookie = this.Helper.getCookie('transparency');
if (transparency_cookie === null) {
//default
config.TRANSPARENCY = false;
}
if (transparency_cookie) {
config.TRANSPARENCY = true;
}
else {
config.TRANSPARENCY = false;
}
//transparency_type
var transparency_type = this.Helper.getCookie('transparency_type');
if (transparency_type === null) {
//default
config.TRANSPARENCY_TYPE = 'squares';
}
if (transparency_type) {
config.TRANSPARENCY_TYPE = transparency_type;
}
//snap
var snap_cookie = this.Helper.getCookie('snap');
if (snap_cookie === null) {
//default
config.SNAP = true;
}
else{
config.SNAP = Boolean(snap_cookie);
}
//guides
var guides_cookie = this.Helper.getCookie('guides');
if (guides_cookie === null) {
//default
config.guides_enabled = true;
}
else{
config.guides_enabled = Boolean(guides_cookie);
}
}
render_main_gui() {
this.autodetect_dimensions();
this.change_theme();
this.prepare_canvas();
this.GUI_tools.render_main_tools();
this.GUI_preview.render_main_preview();
this.GUI_colors.render_main_colors();
this.GUI_layers.render_main_layers();
this.GUI_information.render_main_information();
this.GUI_details.render_main_details();
this.GUI_menu.render_main();
this.load_saved_changes();
this.set_events();
this.load_translations();
}
init_service_worker() {
/*if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./service-worker.js').then(function(reg) {
//Successfully registered service worker
}).catch(function(err) {
console.warn('Error registering service worker', err);
});
}*/
}
set_events() {
var _this = this;
//menu events
this.GUI_menu.on('select_target', (target, object) => {
var parts = target.split('.');
var module = parts[0];
var function_name = parts[1];
var param = object.parameter ??= null;
//call module
if (this.modules[module] == undefined) {
alertify.error('Modules class not found: ' + module);
return;
}
if (this.modules[module][function_name] == undefined) {
alertify.error('Module function not found. ' + module + '.' + function_name);
return;
}
this.modules[module][function_name](param);
});
//registerToggleAbility
var targets = document.querySelectorAll('.toggle');
for (var i = 0; i < targets.length; i++) {
if (targets[i].dataset.target == undefined)
continue;
targets[i].addEventListener('click', function (event) {
this.classList.toggle('toggled');
var target = document.getElementById(this.dataset.target);
target.classList.toggle('hidden');
//save
if (target.classList.contains('hidden') == false)
_this.Helper.setCookie(this.dataset.target, 1);
else
_this.Helper.setCookie(this.dataset.target, 0);
});
}
document.getElementById('left_mobile_menu_button').addEventListener('click', function (event) {
document.querySelector('.sidebar_left').classList.toggle('active');
});
document.getElementById('mobile_menu_button').addEventListener('click', function (event) {
document.querySelector('.sidebar_right').classList.toggle('active');
});
window.addEventListener('resize', function (event) {
//resize
_this.prepare_canvas();
config.need_render = true;
}, false);
this.check_canvas_offset();
//confirmation on exit
var exit_confirm = this.Tools_settings.get_setting('exit_confirm');
window.addEventListener('beforeunload', function (e) {
if(exit_confirm && (config.layers.length > 1 || _this.Base_layers.is_layer_empty(config.layer.id) == false)){
e.preventDefault();
e.returnValue = '';
}
return undefined;
});
document.getElementById('canvas_minipaint').addEventListener('contextmenu', function (e) {
e.preventDefault();
}, false);
}
check_canvas_offset() {
//calc canvas position offset
var bodyRect = document.body.getBoundingClientRect();
var canvas_el = document.getElementById('canvas_minipaint').getBoundingClientRect();
this.canvas_offset.x = canvas_el.left - bodyRect.left;
this.canvas_offset.y = canvas_el.top - bodyRect.top;
}
prepare_canvas() {
var canvas = document.getElementById('canvas_minipaint');
var ctx = canvas.getContext("2d");
var wrapper = document.getElementById('main_wrapper');
var page_w = wrapper.clientWidth;
var page_h = wrapper.clientHeight;
var w = Math.min(Math.ceil(config.WIDTH * config.ZOOM), page_w);
var h = Math.min(Math.ceil(config.HEIGHT * config.ZOOM), page_h);
canvas.width = w;
canvas.height = h;
config.visible_width = w;
config.visible_height = h;
if(config.ZOOM >= 1) {
ctx.imageSmoothingEnabled = false;
}
else{
ctx.imageSmoothingEnabled = true;
}
this.render_canvas_background('canvas_minipaint');
//change wrapper dimensions
document.getElementById('canvas_wrapper').style.width = w + 'px';
document.getElementById('canvas_wrapper').style.height = h + 'px';
this.check_canvas_offset();
}
load_saved_changes() {
var targets = document.querySelectorAll('.toggle');
for (var i = 0; i < targets.length; i++) {
if (targets[i].dataset.target == undefined)
continue;
var target = document.getElementById(targets[i].dataset.target);
var saved = this.Helper.getCookie(targets[i].dataset.target);
if (saved === 0) {
targets[i].classList.toggle('toggled');
target.classList.add('hidden');
}
}
}
load_translations() {
var lang = this.Helper.getCookie('language');
//load from params
var params = this.Helper.get_url_parameters();
if(params.lang != undefined){
lang = params.lang.replace(/([^a-z]+)/gi, '');
}
if (lang != null && lang != config.LANG) {
config.LANG = lang.replace(/([^a-z]+)/gi, '');
this.Tools_translate.translate(config.LANG);
}
}
autodetect_dimensions() {
var wrapper = document.getElementById('main_wrapper');
var page_w = wrapper.clientWidth;
var page_h = wrapper.clientHeight;
var auto_size = false;
//use largest possible
for (var i = this.common_dimensions.length - 1; i >= 0; i--) {
if (this.common_dimensions[i][0] > page_w
|| this.common_dimensions[i][1] > page_h) {
//browser size is too small
continue;
}
config.WIDTH = parseInt(this.common_dimensions[i][0]);
config.HEIGHT = parseInt(this.common_dimensions[i][1]);
auto_size = true;
break;
}
if (auto_size == false) {
//screen size is smaller then 400x300
config.WIDTH = parseInt(page_w) - 15;
config.HEIGHT = parseInt(page_h) - 10;
}
}
render_canvas_background(canvas_id, gap) {
if (gap == undefined)
gap = 10;
var target = document.getElementById(canvas_id + '_background');
if (config.TRANSPARENCY == false) {
target.className = 'transparent-grid white';
return false;
}
else{
target.className = 'transparent-grid ' + config.TRANSPARENCY_TYPE;
}
target.style.backgroundSize = (gap * 2) + 'px auto';
}
draw_grid(ctx) {
if (this.grid == false)
return;
var gap_x = this.grid_size[0];
var gap_y = this.grid_size[1];
var width = config.WIDTH;
var height = config.HEIGHT;
//size
if (gap_x != undefined && gap_y != undefined)
this.grid_size = [gap_x, gap_y];
else {
gap_x = this.grid_size[0];
gap_y = this.grid_size[1];
}
gap_x = parseInt(gap_x);
gap_y = parseInt(gap_y);
ctx.lineWidth = 1;
ctx.beginPath();
if (gap_x < 2)
gap_x = 2;
if (gap_y < 2)
gap_y = 2;
for (var i = gap_x; i < width; i = i + gap_x) {
if (gap_x == 0)
break;
if (i % (gap_x * 5) == 0) {
//main lines
ctx.strokeStyle = '#222222';
}
else {
//small lines
ctx.strokeStyle = '#bbbbbb';
}
ctx.beginPath();
ctx.moveTo(0.5 + i, 0);
ctx.lineTo(0.5 + i, height);
ctx.stroke();
}
for (var i = gap_y; i < height; i = i + gap_y) {
if (gap_y == 0)
break;
if (i % (gap_y * 5) == 0) {
//main lines
ctx.strokeStyle = '#222222';
}
else {
//small lines
ctx.strokeStyle = '#bbbbbb';
}
ctx.beginPath();
ctx.moveTo(0, 0.5 + i);
ctx.lineTo(width, 0.5 + i);
ctx.stroke();
}
}
draw_guides(ctx){
if(config.guides_enabled == false){
return;
}
var thick_guides = this.Tools_settings.get_setting('thick_guides');
for(var i in config.guides) {
var guide = config.guides[i];
if (guide.x === 0 || guide.y === 0) {
continue;
}
//set styles
ctx.strokeStyle = '#00b8b8';
if(thick_guides == false)
ctx.lineWidth = 1;
else
ctx.lineWidth = 3;
ctx.beginPath();
if (guide.y === null) {
//vertical
ctx.moveTo(guide.x, 0);
ctx.lineTo(guide.x, config.HEIGHT);
}
if (guide.x === null) {
//horizontal
ctx.moveTo(0, guide.y);
ctx.lineTo(config.WIDTH, guide.y);
}
ctx.stroke();
}
}
/**
* change draw area size
*
* @param {int} width
* @param {int} height
*/
set_size(width, height) {
config.WIDTH = parseInt(width);
config.HEIGHT = parseInt(height);
this.prepare_canvas();
}
/**
*
* @returns {object} keys: width, height
*/
get_visible_area_size() {
var wrapper = document.getElementById('main_wrapper');
var page_w = wrapper.clientWidth;
var page_h = wrapper.clientHeight;
//find visible size in pixels, but make sure its correct even if image smaller then screen
var w = Math.min(Math.ceil(config.WIDTH * config.ZOOM), Math.ceil(page_w / config.ZOOM));
var h = Math.min(Math.ceil(config.HEIGHT * config.ZOOM), Math.ceil(page_h / config.ZOOM));
return {
width: w,
height: h,
};
}
/**
* change theme or set automatically from cookie if possible
*
* @param {string} theme_name
*/
change_theme(theme_name = null){
if(theme_name == null){
//auto detect
var theme_cookie = this.Helper.getCookie('theme');
if (theme_cookie) {
theme_name = theme_cookie;
}
else {
theme_name = this.Tools_settings.get_setting('theme');
}
}
for(var i in config.themes){
document.querySelector('body').classList.remove('theme-' + config.themes[i]);
}
document.querySelector('body').classList.add('theme-' + theme_name);
}
get_language() {
return config.LANG;
}
get_color() {
return config.COLOR;
}
get_alpha() {
return config.ALPHA;
}
get_zoom() {
return config.ZOOM;
}
get_transparency_support() {
return config.TRANSPARENCY;
}
get_active_tool() {
return config.TOOL;
}
}
export default Base_gui_class;
@@ -0,0 +1,884 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import app from "./../app.js";
import config from "./../config.js";
import Base_gui_class from "./base-gui.js";
import Base_selection_class from "./base-selection.js";
import Image_trim_class from "./../modules/image/trim.js";
import View_ruler_class from "./../modules/view/ruler.js";
import zoomView from "./../libs/zoomView.js";
import Helper_class from "./../libs/helpers.js";
import alertify from "./../../../node_modules/alertifyjs/build/alertify.min.js";
var instance = null;
/**
* Layers class - manages layers. Each layer is object with various types. Keys:
* - id (int)
* - link (image)
* - parent_id (int)
* - name (string)
* - type (string)
* - x (int)
* - y (int)
* - width (int)
* - height (int)
* - width_original (int)
* - height_original (int)
* - visible (bool)
* - is_vector (bool)
* - hide_selection_if_active (bool)
* - opacity (0-100)
* - order (int)
* - composition (string)
* - rotate (int) 0-359
* - data (various data here)
* - params (object)
* - color {hex}
* - status (string)
* - filters (array)
* - render_function (function)
*/
class Base_layers_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_gui = new Base_gui_class();
this.Helper = new Helper_class();
this.Image_trim = new Image_trim_class();
this.View_ruler = new View_ruler_class();
this.canvas = document.getElementById("canvas_minipaint");
this.ctx = document.getElementById("canvas_minipaint").getContext("2d");
this.ctx_preview = document
.getElementById("canvas_preview")
.getContext("2d");
this.last_zoom = 1;
this.auto_increment = 1;
this.stable_dimensions = [];
this.debug_rendering = false;
this.render_success = null;
this.disabled_filter_id = null;
}
/**
* do preparation on start
*/
init() {
this.init_zoom_lib();
new app.Actions.Insert_layer_action({}).do();
var sel_config = {
enable_background: false,
enable_borders: true,
enable_controls: false,
enable_rotation: false,
enable_move: false,
data_function: function () {
return config.layer;
},
};
this.Base_selection = new Base_selection_class(
this.ctx,
sel_config,
"main"
);
this.render(true);
}
init_zoom_lib() {
zoomView.setBounds(0, 0, config.WIDTH, config.HEIGHT);
zoomView.setContext(this.ctx);
this.stable_dimensions = [config.WIDTH, config.HEIGHT];
}
pre_render() {
this.ctx.save();
zoomView.canvasDefault();
this.ctx.clearRect(
0,
0,
config.WIDTH * config.ZOOM,
config.HEIGHT * config.ZOOM
);
}
after_render() {
config.need_render = false;
config.need_render_changed_params = false;
this.ctx.restore();
zoomView.canvasDefault();
}
/**
* renders all layers objects on main canvas
*
* @param {bool} force
*/
render(force) {
var _this = this;
if (force !== true) {
//request render and exit
config.need_render = true;
return;
}
if (
this.stable_dimensions[0] != config.WIDTH ||
this.stable_dimensions[1] != config.HEIGHT
) {
//dimensions changed - re-init zoom lib
this.init_zoom_lib();
}
if (config.need_render == true) {
this.render_success = null;
if (this.debug_rendering === true) {
console.log("Rendering...");
}
if (this.last_zoom != config.ZOOM) {
//change zoom
zoomView.scaleAt(
this.Base_gui.GUI_preview.zoom_data.x,
this.Base_gui.GUI_preview.zoom_data.y,
config.ZOOM / this.last_zoom
);
} else if (this.Base_gui.GUI_preview.zoom_data.move_pos != null) {
//move visible window
var pos = this.Base_gui.GUI_preview.zoom_data.move_pos;
var pos_global = zoomView.toScreen(pos);
zoomView.move(-pos_global.x, -pos_global.y);
this.Base_gui.GUI_preview.zoom_data.move_pos = null;
}
//prepare
this.pre_render();
//take data
var layers_sorted = this.get_sorted_layers();
zoomView.apply();
const newCanvas = this.create_new_canvas(
null,
config.WIDTH,
config.HEIGHT
);
this.render_objects(this.ctx, newCanvas, layers_sorted, ()=>{
this.ctx.save();
});
//grid
this.Base_gui.draw_grid(this.ctx);
//guides
this.Base_gui.draw_guides(this.ctx);
//render selected object controls
this.Base_selection.draw_selection();
//active tool overlay
this.render_overlay();
//render preview
this.render_preview(layers_sorted);
//reset
this.after_render();
this.last_zoom = config.ZOOM;
this.Base_gui.GUI_details.render_details();
this.View_ruler.render_ruler();
if (this.render_success === false) {
alertify.error("Rendered with errors.");
}
}
requestAnimationFrame(function () {
_this.render(force);
});
}
render_overlay() {
var render_class = config.TOOL.name;
var render_function = "render_overlay";
if (
typeof this.Base_gui.GUI_tools.tools_modules[render_class].object[
render_function
] != "undefined"
) {
this.Base_gui.GUI_tools.tools_modules[render_class].object[
render_function
](this.ctx);
}
}
/**
* LEGACY: use create_new_canvas();
*/
createNewCanvas(ctx, h, w) {
this.create_new_canvas(ctx, w, h);
}
/**
* Creates a fresh new canvas with the same height and width as the provided one
* @param {canvas.context|null} ctx
* @param {number} [width]
* @param {number} [height]
*/
create_new_canvas(ctx, width, height) {
const newCanvas = document.createElement("canvas");
if(width){
newCanvas.width = width;
}
else{
newCanvas.width = ctx.canvas.width;
}
if(height){
newCanvas.height = height;
}
else{
newCanvas.height = ctx.canvas.height;
}
return newCanvas;
}
/**
* LEGACY: use render_objects()
*/
renderObjects(ctx, tempCanvas, layers, prepare, shouldSkip) {
this.render_objects(ctx, tempCanvas, layers, prepare, shouldSkip);
}
/**
* Renders objects based on the provided layers
* @param {canvas.context} ctx - Main canvas context where it needs to be rendered
* @param {canvas} tempCanvas - A temporary canvas which is a copy of the original canvas, but will be used if there will be needed to isolate an effect from others
* @param {Object[]} layers - Array of layers
* @param {Function} prepare - An optional function to prepare temporary and main canvases before the render if needed
* @param {Function} shouldSkip - An optional boolean function for skipping those layers which are not needed to be rendered
*/
render_objects(ctx, tempCanvas, layers, prepare, shouldSkip) {
const tempCtx = tempCanvas.getContext("2d");
// Prepare the temporary canvas if needed
prepare && prepare();
for (var i = layers.length - 1; i >= 0; i--) {
var layer = layers[i];
const nextLayer = layers[i - 1];
// If the previous layer has clip masking effect and the current one is not the other end of the pair,
// then render the temporary canvas for clip masking on top of the current.
// Skip the layer if not needed to be rendered
if (shouldSkip && shouldSkip(layer)) {
continue;
}
// If the layer or next layer has clip masking effect (source-atop).
// If there are such layers, this will make sure that layers will be rendered
// in an isolated temporary canvas
if (
layer.composition === "source-atop" ||
(nextLayer && nextLayer.composition === "source-atop")
) {
// Apply the effect in a isolated temporary canvas
tempCtx.globalAlpha = layer.opacity / 100;
tempCtx.globalCompositeOperation = layer.composition;
// If the next layer has the clip masking effect then
// isolated the shadow filter from temporary canvas and keep that in the original canvas
if (nextLayer?.composition === "source-atop") {
// Render the layer
this.render_object(ctx, layer);
// Then remove the shadow (if it exists) from the render process in the temporary canvas
const filters = layer.filters.filter((filter) => {
return filter.name !== "shadow";
});
this.render_object(tempCtx, {
...layer,
filters,
});
} else {
// If we are in this condition, then it means this is the last layer of clipped layers pair.
// Render clipped layers on the temporary canvas
this.render_object(tempCtx, layer);
// Render the clipped layers on top of the current canvas
ctx.restore();
ctx.drawImage(tempCanvas, 0, 0);
// Prepare canvas to since we called restore
prepare && prepare();
// Clear temporary canvas
tempCtx.globalCompositeOperation = null;
tempCtx.clearRect(0, 0, tempCanvas.width, tempCanvas.height);
}
} else {
ctx.globalAlpha = layer.opacity / 100;
ctx.globalCompositeOperation = layer.composition;
this.render_object(ctx, layer);
}
}
}
render_preview(layers) {
var w = this.Base_gui.GUI_preview.PREVIEW_SIZE.w;
var h = this.Base_gui.GUI_preview.PREVIEW_SIZE.h;
this.ctx_preview.save();
this.ctx_preview.clearRect(0, 0, w, h);
const newCanvas = this.create_new_canvas(this.ctx_preview);
newCanvas.getContext("2d").scale(w / config.WIDTH, h / config.HEIGHT);
this.render_objects(this.ctx_preview, newCanvas, layers, () => {
this.ctx_preview.save();
//prepare scale
this.ctx_preview.scale(w / config.WIDTH, h / config.HEIGHT);
});
this.ctx_preview.restore();
this.Base_gui.GUI_preview.render_preview_active_zone();
}
/**
* export current layers to given canvas
*
* @param {canvas.context} ctx
* @param {object} object
* @param {boolean} is_preview
*/
render_object(ctx, object, is_preview) {
if (object.visible == false || object.type == null) return;
this.pre_render_object(ctx, object);
//example with canvas object - other types should overwrite this method
if (object.type == "image") {
//image - default behavior
ctx.save();
ctx.translate(object.x + object.width / 2, object.y + object.height / 2);
ctx.rotate((object.rotate * Math.PI) / 180);
// TODO - Not sure why the check should be with null,
// if nothing will break, then better to check if it's just truthy
ctx.drawImage(
object.link_canvas != null ? object.link_canvas : object.link,
-object.width / 2,
-object.height / 2,
object.width,
object.height
);
ctx.restore();
} else {
//call render function from other module
var render_class = object.render_function[0];
var render_function = object.render_function[1];
if (
typeof this.Base_gui.GUI_tools.tools_modules[render_class] !=
"undefined"
) {
this.Base_gui.GUI_tools.tools_modules[render_class].object[
render_function
](ctx, object, is_preview);
} else {
this.render_success = false;
console.log("Error: unknown layer type: " + object.type);
}
}
this.after_render_object(ctx, object);
}
/**
* Gets called before render_object starts it's job
* @param {canvas.context} ctx
* @param {object} object
*/
pre_render_object(ctx, object) {
//apply pre-filters
for (var i in object.filters) {
var filter = object.filters[i];
if (filter.id == this.disabled_filter_id) {
continue;
}
filter.name = filter.name.replace("drop-shadow", "shadow");
//find filter
var found = false;
for (var i in this.Base_gui.modules) {
if (i.indexOf("effects") == -1 || i.indexOf("abstract") > -1) continue;
var filter_class = this.Base_gui.modules[i];
var module_name = i.split("/").pop();
if (module_name == filter.name) {
//found it
found = true;
filter_class.render_pre(ctx, filter, object);
}
}
if (found == false) {
this.render_success = false;
console.log("Error: can not find filter: " + filter.name);
}
}
}
/**
* Gets called after when render_object finishes it's job
* @param {canvas.context} ctx
* @param {object} object
*/
after_render_object(ctx, object) {
//apply post-filters
for (var i in object.filters) {
var filter = object.filters[i];
if (filter.id == this.disabled_filter_id) {
continue;
}
filter.name = filter.name.replace("drop-shadow", "shadow");
//find filter
var found = false;
for (var i in this.Base_gui.modules) {
if (i.indexOf("effects") == -1 || i.indexOf("abstract") > -1) continue;
var filter_class = this.Base_gui.modules[i];
var module_name = i.split("/").pop();
if (module_name == filter.name) {
//found it
found = true;
filter_class.render_post(ctx, filter, object);
}
}
if (found == false) {
this.render_success = false;
console.log("Error: can not find filter: " + filter.name);
}
}
}
/**
* creates new layer
*
* @param {array} settings
* @param {boolean} can_automate
*/
async insert(settings, can_automate = true) {
return app.State.do_action(
new app.Actions.Insert_layer_action(settings, can_automate)
);
}
/**
* autoresize layer, based on dimensions, up - always, if 1 layer - down.
*
* @param {int} width
* @param {int} height
* @param {int} layer_id
* @param {boolean} can_automate
*/
async autoresize(width, height, layer_id, can_automate = true) {
return app.State.do_action(
new app.Actions.Autoresize_canvas_action(
width,
height,
layer_id,
can_automate
)
);
}
/**
* returns layer
*
* @param {int} id
* @returns {object}
*/
get_layer(id) {
if (id == undefined) {
id = config.layer.id;
}
for (var i in config.layers) {
if (config.layers[i].id == id) {
return config.layers[i];
}
}
alertify.error("Error: can not find layer with id:" + id);
return null;
}
/**
* removes layer
*
* @param {int} id
* @param {boolean} force - Force to delete first layer?
*/
async delete(id, force) {
return app.State.do_action(new app.Actions.Delete_layer_action(id, force));
}
/*
* removes all layers
*/
async reset_layers(auto_insert) {
return app.State.do_action(
new app.Actions.Reset_layers_action(auto_insert)
);
}
/**
* toggle layer visibility
*
* @param {int} id
*/
async toggle_visibility(id) {
return app.State.do_action(
new app.Actions.Toggle_layer_visibility_action(id)
);
}
/*
* renew layers HTML
*/
refresh_gui() {
this.Base_gui.GUI_layers.render_layers();
}
/**
* marks layer as selected, active
*
* @param {int} id
*/
async select(id) {
return app.State.do_action(new app.Actions.Select_layer_action(id));
}
/**
* change layer opacity
*
* @param {int} id
* @param {int} value 0-100
*/
async set_opacity(id, value) {
value = parseInt(value);
if (value < 0 || value > 100) {
//reset
value = 100;
}
return app.State.do_action(
new app.Actions.Update_layer_action(id, {
opacity: value,
})
);
}
/**
* clear layer data
*
* @param {int} id
*/
async layer_clear(id) {
return app.State.do_action(new app.Actions.Clear_layer_action(id));
}
/**
* move layer up or down
*
* @param {int} id
* @param {int} direction
*/
async move(id, direction) {
return app.State.do_action(
new app.Actions.Reorder_layer_action(id, direction)
);
}
/**
* clone and sort.
*/
get_sorted_layers() {
return config.layers.concat().sort(
//sort function
(a, b) => b.order - a.order
);
}
/**
* checks if layer empty
*
* @param {int} id
* @returns {Boolean}
*/
is_layer_empty(id) {
var link = this.get_layer(id);
if (
(link.width == 0 || link.width === null) &&
(link.height == 0 || link.height === null) &&
link.data == null
) {
return true;
}
return false;
}
/**
* find next layer
*
* @param {int} id layer id
* @returns {layer|null}
*/
find_next(id) {
id = parseInt(id);
var link = this.get_layer(id);
var layers_sorted = this.get_sorted_layers();
var last = null;
for (var i = layers_sorted.length - 1; i >= 0; i--) {
var value = layers_sorted[i];
if (last != null && last.id == link.id) {
return value;
}
last = value;
}
return null;
}
/**
* find previous layer
*
* @param {int} id layer id
* @returns {layer|null}
*/
find_previous(id) {
id = parseInt(id);
var link = this.get_layer(id);
var layers_sorted = this.get_sorted_layers();
var last = null;
for (var i in layers_sorted) {
var value = layers_sorted[i];
if (last != null && last.id == link.id) {
return value;
}
last = value;
}
return null;
}
/**
* returns global position, for example if canvas is zoomed, it will convert relative mouse position to absolute
* at 100% zoom.
*
* @param {int} x
* @param {int} y
* @returns {object} keys: x, y
*/
get_world_coords(x, y) {
return zoomView.toWorld(x, y);
}
/**
* register new live filter
*
* @param {int} layer_id
* @param {string} name
* @param {object} params
*/
add_filter(layer_id, name, params) {
return app.State.do_action(
new app.Actions.Add_layer_filter_action(layer_id, name, params)
);
}
/**
* delete live filter
*
* @param {int} layer_id
* @param {string} filter_id
*/
delete_filter(layer_id, filter_id) {
return app.State.do_action(
new app.Actions.Delete_layer_filter_action(layer_id, filter_id)
);
}
/**
* exports all layers to canvas for saving
*
* @param {canvas.context} ctx
* @param {int} layer_id Optional
* @param {boolean} is_preview Optional
*/
convert_layers_to_canvas(ctx, layer_id = null, is_preview = true) {
const newCanvas = this.create_new_canvas(ctx);
const layers_sorted = this.get_sorted_layers();
this.render_objects(ctx, newCanvas, layers_sorted, ()=>{
ctx.save();
}, (value) => {
if (value.visible == false || value.type == null) {
return true;
}
if (layer_id != null && value.id != layer_id) {
return true;
}
});
}
/**
* exports (active) layer to canvas for saving
*
* @param {int} layer_id or current layer by default
* @param {boolean} actual_area used for resized image. Default is false.
* @param {boolean} can_trim default is true
* @returns {canvas}
*/
convert_layer_to_canvas(layer_id, actual_area = false, can_trim) {
if (actual_area == null) actual_area = false;
if (layer_id == null) layer_id = config.layer.id;
var link = this.get_layer(layer_id);
var offset_x = 0;
var offset_y = 0;
//create tmp canvas
var canvas = document.createElement("canvas");
if (actual_area === true && link.type == "image") {
canvas.width = link.width_original;
canvas.height = link.height_original;
can_trim = false;
} else {
canvas.width = Math.max(link.width, config.WIDTH);
canvas.height = Math.max(link.height, config.HEIGHT);
}
//add data
if (actual_area === true && link.type == "image") {
canvas.getContext("2d").drawImage(link.link, 0, 0);
} else {
this.render_object(canvas.getContext("2d"), link);
}
//trim
if ((can_trim == true || can_trim == undefined) && link.type != null) {
var trim_info = this.Image_trim.get_trim_info(layer_id);
if (
trim_info.left > 0 ||
trim_info.top > 0 ||
trim_info.right > 0 ||
trim_info.bottom > 0
) {
offset_x = trim_info.left;
offset_y = trim_info.top;
var w = canvas.width - trim_info.left - trim_info.right;
var h = canvas.height - trim_info.top - trim_info.bottom;
if (w > 1 && h > 1) {
this.Helper.change_canvas_size(canvas, w, h, offset_x, offset_y);
}
}
}
canvas.dataset.x = offset_x;
canvas.dataset.y = offset_y;
return canvas;
}
/**
* updates layer image data
*
* @param {canvas} canvas
* @param {int} layer_id (optional)
*/
update_layer_image(canvas, layer_id) {
return app.State.do_action(
new app.Actions.Update_layer_image_action(canvas, layer_id)
);
}
/**
* returns canvas dimensions.
*
* @returns {object}
*/
get_dimensions() {
return {
width: config.WIDTH,
height: config.HEIGHT,
};
}
/**
* returns all layers
*
* @returns {array}
*/
get_layers() {
return config.layers;
}
/**
* disabled filter by id
*
* @param filter_id
*/
disable_filter(filter_id) {
this.disabled_filter_id = filter_id;
}
/**
* finds layer filter by filter ID
*
* @param filter_id
* @param filter_name
* @param layer_id
* @returns {object}
*/
find_filter_by_id(filter_id, filter_name, layer_id) {
if (typeof layer_id == "undefined") {
var layer = config.layer;
} else {
var layer = this.get_layer(layer_id);
}
var filter = {};
for (var i in layer.filters) {
if (
layer.filters[i].name == filter_name &&
layer.filters[i].id == filter_id
) {
return layer.filters[i].params;
}
}
return filter;
}
}
export default Base_layers_class;
@@ -0,0 +1,166 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../config.js';
import Dialog_class from './../libs/popup.js';
import Base_gui_class from './base-gui.js';
const fuzzysort = require('fuzzysort');
var instance = null;
class Base_search_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.POP = new Dialog_class();
this.Base_gui = new Base_gui_class();
this.db = null;
this.events();
}
events() {
document.addEventListener('keydown', (event) => {
if (this.POP.get_active_instances() > 0) {
return;
}
var code = event.key;
if (code == "F3" || ( (event.ctrlKey == true || event.metaKey) && code == "f")) {
//open
this.search();
event.preventDefault();
}
}, false);
document.addEventListener('input', (event) => {
if(document.querySelector('#pop_data_search') == null){
return;
}
var node = document.querySelector('#global_search_results');
node.innerHTML = '';
var query = event.target.value;
if(query == ''){
return;
}
let results = fuzzysort.go(query, this.db, {
keys: ['title'],
limit: 10,
threshold: -50000,
});
//show
for(var i = 0; i < results.length; i++) {
var item = results[i];
var className = "search-result n" + (i+1);
if(i == 0){
className += " active";
}
node.innerHTML += "<div class='"+className+"' data-key='"+item.obj.key+"'>"
+ fuzzysort.highlight(item[0]) + "</div>";
}
}, false);
//allow to select with arrow keys
document.addEventListener('keydown', function (e) {
if(document.querySelector('#global_search_results') == null
|| document.querySelector('.search-result') == null){
return;
}
var k = e.key;
if (k == "ArrowUp") {
var target = document.querySelector('.search-result.active');
var index = Array.from(target.parentNode.children).indexOf(target);
if(index > 0){
index--;
}
target.classList.remove('active');
var target2 =document.querySelector('#global_search_results').childNodes[index];
target2.classList.add('active');
e.preventDefault();
}
else if (k == "ArrowDown") {
var target = document.querySelector('.search-result.active');
var index = Array.from(target.parentNode.children).indexOf(target);
var total = target.parentNode.childElementCount;
if(index < total - 1){
index++;
}
target.classList.remove('active');
var target2 = document.querySelector('#global_search_results').childNodes[index];
target2.classList.add('active');
e.preventDefault();
}
}, false);
}
search() {
var _this = this;
//init DB
if(this.db === null) {
this.db = Object.keys(this.Base_gui.modules);
for(var i in this.db){
this.db[i] = {
key: this.db[i],
title: this.db[i].replace(/_/i, ' '),
};
}
}
var settings = {
title: 'Search',
params: [
{name: "search", title: "Search:", value: ""},
],
on_load: function (params, popup) {
var node = document.createElement("div");
node.id = 'global_search_results';
node.innerHTML = '';
popup.el.querySelector('.dialog_content').appendChild(node);
},
on_finish: function (params) {
//execute
var target = document.querySelector('.search-result.active');
if(target){
//execute
var key = target.dataset.key;
var class_object = this.Base_gui.modules[key];
var function_name = _this.get_function_from_path(key);
_this.POP.hide();
class_object[function_name]();
}
},
};
this.POP.show(settings);
//on input change
document.getElementById("pop_data_search").select();
}
get_function_from_path(path){
var parts = path.split("/");
var result = parts[parts.length - 1];
result = result.replace(/-/, '_');
return result;
}
}
export default Base_search_class;
@@ -0,0 +1,558 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../config.js';
var instance = null;
var settings_all = [];
const handle_size = 12;
const DRAG_TYPE_TOP = 1;
const DRAG_TYPE_BOTTOM = 2;
const DRAG_TYPE_LEFT = 4;
const DRAG_TYPE_RIGHT = 8;
/**
* Selection class - draws rectangular selection on canvas, can be resized.
*/
class Base_selection_class {
/**
* settings:
* - enable_background
* - enable_borders
* - enable_controls
* - enable_rotation
* - enable_move
* - keep_ratio
*
* @param {ctx} ctx
* @param {object} settings
* @param {string|null} key
*/
constructor(ctx, settings, key = null) {
if (key != null) {
settings_all[key] = settings;
}
//singleton
if (instance) {
return instance;
}
instance = this;
this.ctx = ctx;
this.mouse_lock = null;
this.selected_obj_positions = {};
this.selected_obj_rotate_position = {};
this.selected_object_drag_type = null;
this.click_details = {};
this.is_touch = false;
// True if dragging from inside canvas area
this.is_drag = false;
this.current_angle = null;
this.events();
}
events() {
document.addEventListener('mousedown', (e) => {
this.is_drag = false;
if(this.is_touch == true)
return;
if (!e.target.closest('#main_wrapper'))
return;
this.is_drag = true;
this.selected_object_actions(e);
});
document.addEventListener('mousemove', (e) => {
if(this.is_touch == true)
return;
this.selected_object_actions(e);
});
document.addEventListener('mouseup', (e) => {
if(this.is_touch == true)
return;
this.selected_object_actions(e);
});
// touch
document.addEventListener('touchstart', (event) => {
this.is_drag = false;
this.is_touch = true;
if (!event.target.closest('#main_wrapper'))
return;
this.is_drag = true;
this.selected_object_actions(event);
});
document.addEventListener('touchmove', (event) => {
this.selected_object_actions(event);
}, {passive: false});
document.addEventListener('touchend', (event) => {
this.selected_object_actions(event);
});
}
set_selection(x, y, width, height) {
var settings = this.find_settings();
if (x != null)
settings.data.x = x;
if (y != null)
settings.data.y = y;
if (width != null)
settings.data.width = width;
if (height != null)
settings.data.height = height;
config.need_render = true;
}
reset_selection() {
var settings = this.find_settings();
settings.data = {
x: null,
y: null,
width: null,
height: null,
};
config.need_render = true;
}
get_selection() {
var settings = this.find_settings();
return settings.data;
}
find_settings() {
var current_key = config.TOOL.name;
var settings = null;
for (var i in settings_all) {
if (i == current_key)
settings = settings_all[i];
}
//default
if (settings === null) {
settings = settings_all['main'];
}
//find data
settings.data = (settings.data_function).call();
return settings;
}
calcRotateDistanceFromX(layerW) {
const block_size = handle_size / config.ZOOM;
return Math.max(
Math.min(layerW * 0.9, Math.abs(layerW - 2 * block_size)),
layerW / 2 - block_size / 2
);
}
/**
* marks object as selected, and draws corners
*/
draw_selection() {
var settings = this.find_settings();
var data = settings.data;
if (settings.data === null || settings.data.status == 'draft'
|| (settings.data.hide_selection_if_active === true && settings.data.type == config.TOOL.name)) {
return;
}
var x = settings.data.x;
var y = settings.data.y;
var w = settings.data.width;
var h = settings.data.height;
if (x == null || y == null || w == null || h == null) {
//not supported
return;
}
var block_size_default = handle_size / config.ZOOM;
if (config.ZOOM != 1) {
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
}
var block_size = block_size_default;
var corner_offset = (block_size / 2.4);
var middle_offset = (block_size / 1.9);
this.ctx.save();
this.ctx.globalAlpha = 1;
let isRotated = false;
if (data.rotate != null && data.rotate != 0) {
//rotate
isRotated = true;
this.ctx.translate(data.x + data.width / 2, data.y + data.height / 2);
this.ctx.rotate(data.rotate * Math.PI / 180);
x = Math.round(-data.width / 2);
y = Math.round(-data.height / 2);
}
//fill
if (settings.enable_background == true) {
this.ctx.fillStyle = "rgba(0, 255, 0, 0.3)";
this.ctx.fillRect(x, y, w, h);
}
const wholeLineWidth = 2 / config.ZOOM;
const halfLineWidth = wholeLineWidth / 2;
//borders
if (settings.enable_borders == true && (x != 0 || y != 0 || w != config.WIDTH || h != config.HEIGHT)) {
this.ctx.lineWidth = wholeLineWidth;
this.ctx.strokeStyle = 'rgb(255, 255, 255)';
this.ctx.strokeRect(x - halfLineWidth, y - halfLineWidth, w + wholeLineWidth, h + wholeLineWidth);
this.ctx.lineWidth = halfLineWidth;
this.ctx.strokeStyle = 'rgb(0, 0, 0)';
this.ctx.strokeRect(x - wholeLineWidth, y - wholeLineWidth, w + (wholeLineWidth * 2), h + (wholeLineWidth * 2));
}
//show crop lines
if(settings.crop_lines === true){
for(var part = 1; part < 3; part++) {
this.ctx.lineWidth = wholeLineWidth;
this.ctx.strokeStyle = 'rgb(255, 255, 255)';
this.ctx.beginPath();
this.ctx.moveTo(x + w / 3 * part - halfLineWidth, y);
this.ctx.lineTo(x + w / 3 * part - halfLineWidth, y + h);
this.ctx.stroke();
this.ctx.lineWidth = halfLineWidth;
this.ctx.strokeStyle = 'rgb(0, 0, 0)';
this.ctx.beginPath();
this.ctx.moveTo(x + w / 3 * part - halfLineWidth, y);
this.ctx.lineTo(x + w / 3 * part - halfLineWidth, y + h);
this.ctx.stroke();
}
for(var part = 1; part < 3; part++) {
this.ctx.lineWidth = wholeLineWidth;
this.ctx.strokeStyle = 'rgb(255, 255, 255)';
this.ctx.beginPath();
this.ctx.moveTo(x, y + h / 3 * part - halfLineWidth);
this.ctx.lineTo(x + w, y + h / 3 * part - halfLineWidth);
this.ctx.stroke();
this.ctx.lineWidth = halfLineWidth;
this.ctx.strokeStyle = 'rgb(0, 0, 0)';
this.ctx.beginPath();
this.ctx.moveTo(x, y + h / 3 * part - halfLineWidth);
this.ctx.lineTo(x + w, y + h / 3 * part - halfLineWidth);
this.ctx.stroke();
}
}
const hitsLeftEdge = isRotated ? false : x < handle_size;
const hitsTopEdge = isRotated ? false : y < handle_size;
const hitsRightEdge = isRotated ? false : x + w > config.WIDTH - handle_size;
const hitsBottomEdge = isRotated ? false : y + h > config.HEIGHT - handle_size;
//draw corners
var corner = (x, y, dx, dy, drag_type, cursor) => {
var angle = 0;
if (settings.data.rotate != null && settings.data.rotate != 0) {
angle = settings.data.rotate;
}
if (settings.enable_controls == false || angle != 0) {
this.ctx.strokeStyle = "rgba(0, 0, 0, 0.4)";
this.ctx.fillStyle = "rgba(255, 255, 255, 0.8)";
}
else {
this.ctx.strokeStyle = "#000000";
this.ctx.fillStyle = "#ffffff";
}
this.ctx.lineWidth = wholeLineWidth;
//create path
const circle = new Path2D();
circle.arc(x + dx * block_size, y + dy * block_size, block_size / 2, 0, 2 * Math.PI);
//draw
this.ctx.fill(circle);
this.ctx.stroke(circle);
//register position
this.selected_obj_positions[drag_type] = {
cursor: cursor,
path: circle,
};
};
//draw rotation
var draw_rotation = () => {
var settings = this.find_settings();
if (settings.data === null
|| settings.data.status == 'draft'
|| settings.data.rotate === null
|| (settings.data.hide_selection_if_active === true && settings.data.type == config.TOOL.name)) {
return;
}
var r_x = x + this.calcRotateDistanceFromX(w) + corner_offset + wholeLineWidth;
var r_y = y - corner_offset - wholeLineWidth;
var r_dx = hitsRightEdge ? -0.5 : 0;
var r_dy = hitsTopEdge ? 0.5 : 0;
this.ctx.strokeStyle = "#000000";
this.ctx.fillStyle = "#d0d62a";
this.ctx.lineWidth = wholeLineWidth;
//create path
const circle = new Path2D();
circle.arc(r_x + r_dx * block_size, r_y + r_dy * block_size, block_size / 2, 0, 2 * Math.PI);
//draw
this.ctx.fill(circle);
this.ctx.stroke(circle);
//register position
this.selected_obj_rotate_position = {
cursor: "pointer",
path: circle,
};
};
if (settings.enable_rotation == true) {
draw_rotation();
}
if (settings.enable_controls == true) {
corner(x - corner_offset - wholeLineWidth, y - corner_offset - wholeLineWidth, hitsLeftEdge ? 0.5 : 0, hitsTopEdge ? 0.5 : 0, DRAG_TYPE_LEFT | DRAG_TYPE_TOP, 'nwse-resize');
corner(x + w + corner_offset + wholeLineWidth, y - corner_offset - wholeLineWidth, hitsRightEdge ? -0.5 : 0, hitsTopEdge ? 0.5 : 0, DRAG_TYPE_RIGHT | DRAG_TYPE_TOP, 'nesw-resize');
corner(x - corner_offset - wholeLineWidth, y + h + corner_offset + wholeLineWidth, hitsLeftEdge ? 0.5 : 0, hitsBottomEdge ? -0.5 : 0, DRAG_TYPE_LEFT | DRAG_TYPE_BOTTOM, 'nesw-resize');
corner(x + w + corner_offset + wholeLineWidth, y + h + corner_offset + wholeLineWidth, hitsRightEdge ? -0.5 : 0, hitsBottomEdge ? -0.5 : 0, DRAG_TYPE_RIGHT | DRAG_TYPE_BOTTOM, 'nwse-resize');
}
if (settings.enable_controls == true) {
//draw centers
if (Math.abs(w) > block_size * 5) {
corner(x + w / 2, y - middle_offset - wholeLineWidth, 0, hitsTopEdge ? 0.5 : 0, DRAG_TYPE_TOP, 'ns-resize');
corner(x + w / 2, y + h + middle_offset + wholeLineWidth, 0, hitsBottomEdge ? -0.5 : 0, DRAG_TYPE_BOTTOM, 'ns-resize');
}
if (Math.abs(h) > block_size * 5) {
corner(x - middle_offset - wholeLineWidth, y + h / 2, hitsLeftEdge ? 0.5 : 0, 0, DRAG_TYPE_LEFT, 'ew-resize');
corner(x + w + middle_offset + wholeLineWidth, y + h / 2, hitsRightEdge ? -0.5 : 0, 0, DRAG_TYPE_RIGHT, 'ew-resize');
}
}
//restore
this.ctx.restore();
}
selected_object_actions(e) {
var settings = this.find_settings();
var data = settings.data;
if(data == null){
return;
}
this.ctx.save();
if (data.rotate != null && data.rotate != 0) {
this.ctx.translate(data.x + data.width / 2, data.y + data.height / 2);
this.ctx.rotate(data.rotate * Math.PI / 180);
}
var x = settings.data.x;
var y = settings.data.y;
var w = settings.data.width;
var h = settings.data.height;
//simplify checks
var event_type = e.type;
if(event_type == 'touchstart') event_type = 'mousedown';
if(event_type == 'touchmove') event_type = 'mousemove';
if(event_type == 'touchend') event_type = 'mouseup';
if (!this.is_drag && ['mousedown', 'mouseup'].includes(event_type))
return;
const mainWrapper = document.getElementById('main_wrapper');
const defaultCursor = config.TOOL && config.TOOL.name === 'text' ? 'text' : 'default';
if (mainWrapper.style.cursor != defaultCursor) {
mainWrapper.style.cursor = defaultCursor;
}
if (event_type == 'mousedown' && config.mouse.valid == false || settings.enable_controls == false) {
return;
}
var mouse = config.mouse;
const drag_type = this.selected_object_drag_type;
if(event_type == 'mousedown' && settings.data !== null){
this.click_details = {
x: settings.data.x,
y: settings.data.y,
width: settings.data.width,
height: settings.data.height,
};
this.current_angle = null;
}
if (event_type == 'mousemove' && this.mouse_lock == 'selected_object_actions' && this.is_drag) {
const allowNegativeDimensions = settings.data.render_function
&& ['line', 'arrow', 'gradient'].includes(settings.data.render_function[0]);
mainWrapper.style.cursor = "pointer";
var is_ctrl = false;
if (e.ctrlKey == true || e.metaKey) {
is_ctrl = true;
}
const is_drag_type_left = Math.floor(drag_type / DRAG_TYPE_LEFT) % 2 === 1;
const is_drag_type_right = Math.floor(drag_type / DRAG_TYPE_RIGHT) % 2 === 1;
const is_drag_type_top = Math.floor(drag_type / DRAG_TYPE_TOP) % 2 === 1;
const is_drag_type_bottom = Math.floor(drag_type / DRAG_TYPE_BOTTOM) % 2 === 1;
if(is_drag_type_left && is_drag_type_top) mainWrapper.style.cursor = "nwse-resize";
else if(is_drag_type_top && is_drag_type_right) mainWrapper.style.cursor = "nesw-resize";
else if(is_drag_type_right && is_drag_type_bottom) mainWrapper.style.cursor = "nwse-resize";
else if(is_drag_type_bottom && is_drag_type_left) mainWrapper.style.cursor = "nesw-resize";
else if(is_drag_type_top) mainWrapper.style.cursor = "ns-resize";
else if(is_drag_type_right) mainWrapper.style.cursor = "ew-resize";
else if(is_drag_type_bottom) mainWrapper.style.cursor = "ns-resize";
else if(is_drag_type_left) mainWrapper.style.cursor = "ew-resize";
if(drag_type == 'rotate'){
//rotate
var dx = x + this.calcRotateDistanceFromX(w) - (x + w / 2);
var dy = h / 2;
var original_angle = Math.atan2(dy, dx) / Math.PI * 180; //compensate rotation icon angle
var dx = mouse.x - (x + w / 2);
var dy = mouse.y - (y + h / 2);
var angle = Math.atan2(dy, dx) / Math.PI * 180 + original_angle;
//settings.data.rotate = angle;
this.current_angle = angle;
config.need_render = true;
}
else if (e.buttons == 1 || typeof e.buttons == "undefined") {
// Do transformations
var dx = Math.round(mouse.x - mouse.click_x);
var dy = Math.round(mouse.y - mouse.click_y);
var width = this.click_details.width + dx;
var height = this.click_details.height + dy;
if (is_drag_type_top)
height = this.click_details.height - dy;
if (is_drag_type_left)
width = this.click_details.width - dx;
// Keep ratio - (if drag_type power of 2, only dragging on single axis)
if (drag_type && (drag_type & (drag_type - 1)) !== 0 && (settings.keep_ratio == true && is_ctrl == false)
|| (settings.keep_ratio !== true && is_ctrl == true)){
var ratio = this.click_details.width / this.click_details.height;
var width_new = Math.round(height * ratio);
var height_new = Math.round(width / ratio);
if (Math.abs(width * 100 / width_new) > Math.abs(height * 100 / height_new)) {
height = height_new;
}
else {
width = width_new;
}
}
// Set values
settings.data.x = this.click_details.x;
settings.data.y = this.click_details.y;
if (is_drag_type_top)
settings.data.y = this.click_details.y - (height - this.click_details.height);
if (is_drag_type_left)
settings.data.x = this.click_details.x - (width - this.click_details.width);
if (is_drag_type_left || is_drag_type_right)
settings.data.width = width;
if (is_drag_type_top || is_drag_type_bottom)
settings.data.height = height;
// Don't allow negative width/height on most layers
if (!allowNegativeDimensions) {
if (settings.data.width <= 0) {
settings.data.width = Math.abs(settings.data.width);
if (is_drag_type_left) {
settings.data.x -= settings.data.width;
} else {
settings.data.x = this.click_details.x - settings.data.width;
}
}
if (settings.data.height <= 0) {
settings.data.height = Math.abs(settings.data.height);
if (is_drag_type_top) {
settings.data.y -= settings.data.height;
} else {
settings.data.y = this.click_details.y - settings.data.height;
}
}
}
config.need_render = true;
}
return;
}
if (event_type == 'mouseup' && this.mouse_lock == 'selected_object_actions') {
//reset
this.mouse_lock = null;
}
if (!this.mouse_lock) {
//set mouse move cursor
if(settings.enable_move && mouse.x > x && mouse.x < x + w && mouse.y > y && mouse.y < y + h){
mainWrapper.style.cursor = "move";
}
for (let current_drag_type in this.selected_obj_positions) {
const position = this.selected_obj_positions[current_drag_type];
if (position.path && this.ctx.isPointInPath(position.path, mouse.x, mouse.y)) {
// match
if (event_type == 'mousedown') {
if (e.buttons == 1 || typeof e.buttons == "undefined") {
this.mouse_lock = 'selected_object_actions';
this.selected_object_drag_type = current_drag_type;
}
}
if (event_type == 'mousemove') {
mainWrapper.style.cursor = position.cursor;
}
}
}
//rotate?
const position = this.selected_obj_rotate_position;
if (position.path && this.ctx.isPointInPath(position.path, mouse.x, mouse.y)) {
//match
if (event_type == 'mousedown') {
if (e.buttons == 1 || typeof e.buttons == "undefined") {
this.mouse_lock = 'selected_object_actions';
this.selected_object_drag_type = "rotate";
}
}
if (event_type == 'mousemove') {
mainWrapper.style.cursor = position.cursor;
}
}
this.ctx.restore();
}
}
}
export default Base_selection_class;
@@ -0,0 +1,222 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../config.js';
import Base_layers_class from './base-layers.js';
import Base_gui_class from './base-gui.js';
import Helper_class from './../libs/helpers.js';
import alertify from './../../../node_modules/alertifyjs/build/alertify.min.js';
import app from '../app.js';
var instance = null;
/**
* Undo state class. Supports multiple levels undo.
*/
class Base_state_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.Helper = new Helper_class();
this.layers_archive = [];
this.levels = 3;
this.levels_optimal = 3;
this.enabled = true;
this.action_history = [];
this.action_history_index = 0;
this.action_history_max = 50;
this.set_events();
}
set_events() {
document.addEventListener('keydown', (event) => {
const key = (event.key || '').toLowerCase();
if (this.Helper.is_input(event.target))
return;
if (key == "z" && (event.ctrlKey == true || event.metaKey)) {
// Undo
this.undo();
event.preventDefault();
}
if (key == "y" && (event.ctrlKey == true || event.metaKey)) {
// Redo
this.redo();
event.preventDefault();
}
}, false);
}
async do_action(action, options = {}) {
let error_during_free = false;
try {
await action.do();
} catch (error) {
// Action aborted. This is usually expected behavior as actions throw errors if they shouldn't run.
return { status: 'aborted', reason: error };
}
// Remove all redo actions from history
if (this.action_history_index < this.action_history.length) {
const freed_actions = this.action_history.slice(this.action_history_index, this.action_history.length).reverse();
this.action_history = this.action_history.slice(0, this.action_history_index);
for (let freed_action of freed_actions) {
try {
await freed_action.free();
} catch (error) {
error_during_free = true;
}
}
}
// Add the new action to history
const last_action = this.action_history[this.action_history.length - 1];
if (options.merge_with_history && last_action) {
if (typeof options.merge_with_history === 'string') {
options.merge_with_history = [options.merge_with_history];
}
if (options.merge_with_history.includes(last_action.action_id)) {
this.action_history[this.action_history.length - 1] = new app.Actions.Bundle_action(
last_action.action_id,
last_action.action_description,
[last_action, action]
);
}
} else {
this.action_history.push(action);
if (this.action_history.length > this.action_history_max) {
let action_to_free = this.action_history.shift();
try {
await action_to_free.free();
} catch (error) {
error_during_free = true;
}
} else {
this.action_history_index++;
}
}
// Chrome arbitrary method to determine memory usage, but most people use Chrome so...
if (window.performance && window.performance.memory) {
if (window.performance.memory.usedJSHeapSize > window.performance.memory.jsHeapSizeLimit * 0.8) {
this.free(window.performance.memory.jsHeapSizeLimit * 0.2);
}
}
if (error_during_free) {
alertify.error('A problem occurred while removing undo history. It\'s suggested you save your work and refresh the page in order to free up memory.');
}
return { status: 'completed' };
}
can_redo() {
return this.action_history_index < this.action_history.length;
}
can_undo() {
return this.action_history_index > 0;
}
async redo_action() {
if (this.can_redo()) {
const action = this.action_history[this.action_history_index];
await action.do();
this.action_history_index++;
} else {
alertify.success('There\'s nothing to redo', 3);
}
}
async undo_action() {
if (this.can_undo()) {
this.action_history_index--;
await this.action_history[this.action_history_index].undo();
} else {
alertify.success('There\'s nothing to undo', 3);
}
}
async scrap_last_action() {
if (this.can_undo()) {
await this.undo_action();
this.action_history.pop();
}
}
// Frees history actions up to the specified memory & database size. Starts with undo history, then moves to redo history.
async free(memory_size = 0, database_size = 0) {
let total_memory_freed = 0;
let total_database_freed = 0;
let has_error = false;
let free_complete = false;
while (this.action_history_index > 0) {
let action = this.action_history.shift();
total_memory_freed += action.memory_estimate;
total_database_freed += action.database_estimate;
try {
await action.free();
} catch (error) {
has_error = true;
}
if (total_memory_freed >= memory_size && total_database_freed >= database_size) {
free_complete = true;
break;
}
this.action_history_index--;
}
if (!free_complete) {
for (let i = this.action_history.length - 1; i >= 0; i--) {
let action = this.action_history[i];
total_memory_freed += action.memory_estimate;
total_database_freed += action.database_estimate;
try {
await action.free();
} catch (error) {
has_error = true;
}
if (total_memory_freed >= memory_size && total_database_freed >= database_size) {
free_complete = true;
break;
}
}
}
if (has_error) {
alertify.error('A problem occurred while removing undo history. It\'s suggested you save your work and refresh the page in order to free up memory.');
}
return {
total_memory_freed,
total_database_freed
}
}
save() {
const message = 'window.State.save() is removed. Use State.do_action() to manage undo history instead.';
console.warn(message);
alertify.error(message);
}
/**
* supports multiple levels undo system
*/
undo() {
this.undo_action();
}
/**
* supports multiple levels redo system
*/
redo() {
this.redo_action();
}
}
export default Base_state_class;
@@ -0,0 +1,734 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../config.js';
import Base_layers_class from './base-layers.js';
import Base_gui_class from './base-gui.js';
import app from "../app";
import Helper_class from "../libs/helpers";
/**
* Base tools class, can be used for extending on tools like brush, provides various helping methods.
*/
class Base_tools_class {
constructor(save_mouse) {
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.Helper = new Helper_class();
this.is_drag = false;
this.mouse_last_click_pos = [false, false];
this.mouse_click_pos = [false, false];
this.mouse_move_last = [false, false];
this.mouse_valid = false;
this.mouse_click_valid = false;
this.speed_average = 0;
this.save_mouse = save_mouse;
this.is_touch = false;
this.shape_mouse_click = {x: null, y: null};
this.prepare();
if (this.save_mouse == true) {
this.events();
}
}
dragStart(event) {
var _this = this;
var mouse = _this.get_mouse_info(event, true);
_this.mouse_click_pos[0] = mouse.x;
_this.mouse_click_pos[1] = mouse.y;
//update
_this.set_mouse_info(event);
_this.is_drag = true;
_this.speed_average = 0;
var mouse = _this.get_mouse_info(event, true);
_this.mouse_last_click_pos[0] = mouse.x;
_this.mouse_last_click_pos[1] = mouse.y;
}
dragMove(event) {
var _this = this;
_this.set_mouse_info(event);
_this.speed_average = _this.calc_average_mouse_speed(event);
}
dragEnd(event) {
var _this = this;
_this.is_drag = false;
_this.set_mouse_info(event);
}
events() {
var _this = this;
//collect mouse info
document.addEventListener('mousedown', function (event) {
if(_this.is_touch == true)
return;
_this.dragStart(event);
});
document.addEventListener('mousemove', function (event) {
if(_this.is_touch == true)
return;
_this.dragMove(event);
});
document.addEventListener('mouseup', function (event) {
if(_this.is_touch == true)
return;
_this.dragEnd(event);
});
// collect touch info
document.addEventListener('touchstart', function (event) {
_this.is_touch = true;
_this.dragStart(event);
});
document.addEventListener('touchmove', function (event) {
_this.dragMove(event);
if (event.target.id === "canvas_minipaint" && !$('.scroll').has($(event.target)).length)
event.preventDefault();
}, {passive: false});
document.addEventListener('touchend', function (event) {
_this.dragEnd(event);
});
//on resize
window.addEventListener('resize', function (event) {
_this.prepare();
});
}
/**
* do preparation
*/
prepare() {
this.is_drag = config.mouse.is_drag;
}
set_mouse_info(event) {
if (this.save_mouse !== true) {
//not main
return false;
}
var eventType = event.type;
if (event.target.id != 'canvas_minipaint' && event.target.id != 'main_wrapper') {
//outside canvas
this.mouse_valid = false;
}
else {
this.mouse_valid = true;
}
if (eventType === 'mousedown' || eventType === 'touchstart') {
if ((event.target.id != 'canvas_minipaint' && event.target.id != 'main_wrapper') || (event.which != 1 && eventType !== 'touchstart')) {
this.mouse_click_valid = false;
}
else {
this.mouse_click_valid = true;
}
this.mouse_valid = true;
}
if (event.changedTouches) {
//using touch events
event = event.changedTouches[0];
}
var mouse_coords = this.get_mouse_coordinates_from_event(event);
var mouse_x = mouse_coords.x;
var mouse_y = mouse_coords.y;
var start_pos = this.Base_layers.get_world_coords(0, 0);
var x_rel = mouse_x - start_pos.x;
var y_rel = mouse_y - start_pos.y;
//save
config.mouse = {
x: mouse_x,
y: mouse_y,
x_rel: x_rel,
y_rel: y_rel,
last_click_x: this.mouse_last_click_pos[0], //last click
last_click_y: this.mouse_last_click_pos[1], //last click
click_x: this.mouse_click_pos[0],
click_y: this.mouse_click_pos[1],
last_x: this.mouse_move_last[0],
last_y: this.mouse_move_last[1],
valid: this.mouse_valid,
click_valid: this.mouse_click_valid,
is_drag: this.is_drag,
speed_average: this.speed_average,
};
if (eventType === 'mousemove' || eventType === 'touchmove') {
//save last pos
this.mouse_move_last[0] = mouse_x;
this.mouse_move_last[1] = mouse_y;
}
}
get_mouse_coordinates_from_event(event){
var mouse_x = event.pageX - this.Base_gui.canvas_offset.x;
var mouse_y = event.pageY - this.Base_gui.canvas_offset.y;
//adapt coords to ZOOM
var global_pos = this.Base_layers.get_world_coords(mouse_x, mouse_y);
mouse_x = global_pos.x;
mouse_y = global_pos.y;
return {
x: mouse_x,
y: mouse_y,
};
}
get_mouse_info(event) {
if(typeof event != "undefined" && typeof mouse.x == "undefined"){
//mouse not set yet - set it now...
this.set_mouse_info(event);
}
return config.mouse;
}
calc_average_mouse_speed(event) {
if (this.is_drag == false)
return null;
//calc average speed
var avg_speed_max = 30;
var avg_speed_changing_power = 2;
var mouse = this.get_mouse_info(event, true);
var dx = Math.abs(mouse.x - mouse.last_x);
var dy = Math.abs(mouse.y - mouse.last_y);
var delta = Math.sqrt(dx * dx + dy * dy);
var mouse_average_speed = this.speed_average;
if (delta > avg_speed_max / 2) {
mouse_average_speed += avg_speed_changing_power;
}
else {
mouse_average_speed -= avg_speed_changing_power;
}
mouse_average_speed = Math.max(0, mouse_average_speed); //min
mouse_average_speed = Math.min(avg_speed_max, mouse_average_speed); //max
return mouse_average_speed;
}
get_params_hash() {
var data = [
this.getParams(),
config.COLOR,
config.ALPHA,
];
return JSON.stringify(data);
}
clone(object) {
return JSON.parse(JSON.stringify(object));
}
/**
* customized mouse cursor
*
* @param {int} x
* @param {int} y
* @param {int} size
* @param {string} type circle, rect
*/
show_mouse_cursor(x, y, size, type) {
//fix coordinates, because of scroll
var start_pos = this.Base_layers.get_world_coords(0, 0);
x = x - start_pos.x;
y = y - start_pos.y;
var element = document.getElementById('mouse');
size = size * config.ZOOM;
x = x * config.ZOOM;
y = y * config.ZOOM;
if (size < 5) {
//too small
element.className = '';
return;
}
element.style.width = size + 'px';
element.style.height = size + 'px';
element.style.left = x - Math.ceil(size / 2) + 'px';
element.style.top = y - Math.ceil(size / 2) + 'px';
//add style
element.className = '';
element.classList.add(type);
}
getParams() {
const params = {};
// Number inputs return the .value if defined as objects.
for (let attributeName in config.TOOL.attributes) {
const attribute = config.TOOL.attributes[attributeName];
if (!isNaN(attribute.value) && attribute.value != null) {
if (typeof attribute.value === 'string') {
params[attributeName] = attribute;
} else {
params[attributeName] = attribute.value;
}
} else {
params[attributeName] = attribute;
}
}
return params;
}
adaptSize(value, type = "width") {
var response;
if (config.layer.width_original == null) {
return value;
}
if (type === "width") {
response = value / (config.layer.width / config.layer.width_original);
}
else {
response = value / (config.layer.height / config.layer.height_original);
}
return response;
}
draw_shape(ctx, x, y, width, height, coords, is_demo) {
if(is_demo !== false) {
ctx.fillStyle = '#aaa';
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
}
ctx.lineJoin = "round";
ctx.beginPath();
for(var i in coords){
if(coords[i] === null){
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.beginPath();
continue;
}
//coords in 100x100 box
var pos_x = x + coords[i][0] * width / 100;
var pos_y = y + coords[i][1] * height / 100;
if(i == '0')
ctx.moveTo(pos_x, pos_y);
else
ctx.lineTo(pos_x, pos_y);
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
default_events(){
var _this = this;
//mouse events
document.addEventListener('mousedown', function (event) {
_this.default_dragStart(event);
});
document.addEventListener('mousemove', function (event) {
_this.default_dragMove(event);
});
document.addEventListener('mouseup', function (event) {
_this.default_dragEnd(event);
});
// collect touch events
document.addEventListener('touchstart', function (event) {
_this.default_dragStart(event);
});
document.addEventListener('touchmove', function (event) {
_this.default_dragMove(event);
});
document.addEventListener('touchend', function (event) {
_this.default_dragEnd(event);
});
}
default_dragStart(event) {
if (config.TOOL.name != this.name)
return;
this.mousedown(event);
}
default_dragMove(event) {
if (config.TOOL.name != this.name)
return;
this.mousemove(event);
}
default_dragEnd(event) {
if (config.TOOL.name != this.name)
return;
this.mouseup(event);
}
shape_mousedown(e) {
var mouse = this.get_mouse_info(e);
if (mouse.click_valid == false)
return;
var mouse_x = mouse.x;
var mouse_y = mouse.y;
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.shape_mouse_click.x = mouse_x;
this.shape_mouse_click.y = mouse_y;
//register new object - current layer is not ours or params changed
this.layer = {
type: this.name,
params: this.clone(this.getParams()),
status: 'draft',
render_function: [this.name, 'render'],
x: Math.round(mouse_x),
y: Math.round(mouse_y),
color: null,
is_vector: true
};
app.State.do_action(
new app.Actions.Bundle_action('new_'+this.name+'_layer', 'New '+this.Helper.ucfirst(this.name)+' Layer', [
new app.Actions.Insert_layer_action(this.layer)
])
);
}
shape_mousemove(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.is_drag == false)
return;
if (mouse.click_valid == false) {
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.shape_mouse_click.x);
var click_y = Math.round(this.shape_mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
var x = Math.min(mouse_x, click_x);
var y = Math.min(mouse_y, click_y);
var width = Math.abs(mouse_x - click_x);
var height = Math.abs(mouse_y - click_y);
if (e.ctrlKey == true || e.metaKey) {
if (width < height * this.best_ratio) {
width = height * this.best_ratio;
}
else {
height = width / this.best_ratio;
}
if (mouse_x < click_x) {
x = click_x - width;
}
if (mouse_y < click_y) {
y = click_y - height;
}
}
//more data
config.layer.x = x;
config.layer.y = y;
config.layer.width = width;
config.layer.height = height;
this.Base_layers.render();
}
shape_mouseup(e) {
var mouse = this.get_mouse_info(e);
var params = this.getParams();
if (mouse.click_valid == false) {
config.layer.status = null;
return;
}
var mouse_x = Math.round(mouse.x);
var mouse_y = Math.round(mouse.y);
var click_x = Math.round(this.shape_mouse_click.x);
var click_y = Math.round(this.shape_mouse_click.y);
//apply snap
var snap_info = this.calc_snap_position(e, mouse_x, mouse_y, config.layer.id);
if(snap_info != null){
if(snap_info.x != null) {
mouse_x = snap_info.x;
}
if(snap_info.y != null) {
mouse_y = snap_info.y;
}
}
this.snap_line_info = {x: null, y: null};
var x = Math.min(mouse_x, click_x);
var y = Math.min(mouse_y, click_y);
var width = Math.abs(mouse_x - click_x);
var height = Math.abs(mouse_y - click_y);
if (e.ctrlKey == true || e.metaKey) {
if (width < height * this.best_ratio) {
width = height * this.best_ratio;
}
else {
height = width / this.best_ratio;
}
if (mouse_x < click_x) {
x = click_x - width;
}
if (mouse_y < click_y) {
y = click_y - height;
}
}
if (width == 0 && height == 0) {
//same coordinates - cancel
app.State.scrap_last_action();
return;
}
//more data
app.State.do_action(
new app.Actions.Update_layer_action(config.layer.id, {
x,
y,
width,
height,
status: null
}),
{ merge_with_history: 'new_'+this.name+'_layer' }
);
}
render_overlay_parent(ctx){
//x
if(this.snap_line_info.x !== null) {
this.Helper.draw_special_line(
ctx,
this.snap_line_info.x.start_x,
this.snap_line_info.x.start_y,
this.snap_line_info.x.end_x,
this.snap_line_info.x.end_y
);
}
//y
if(this.snap_line_info.y !== null) {
this.Helper.draw_special_line(
ctx,
this.snap_line_info.y.start_x,
this.snap_line_info.y.start_y,
this.snap_line_info.y.end_x,
this.snap_line_info.y.end_y
);
}
}
get_snap_positions(exclude_id) {
var snap_positions = {
x: [
0,
config.WIDTH/2,
config.WIDTH,
],
y: [
0,
config.HEIGHT/2,
config.HEIGHT,
],
};
if(config.guides_enabled == true){
//use guides
for(var i in config.guides){
var guide = config.guides[i];
if(guide.y === null)
snap_positions.x.push(guide.x);
else
snap_positions.y.push(guide.y);
}
}
for(var i in config.layers){
if(exclude_id != null && exclude_id == config.layers[i].id){
continue;
}
if(config.layers[i].visible == false
|| config.layers[i].x === null || config.layers[i].y === null
|| config.layers[i].width === null || config.layers[i].height === null){
continue;
}
//x
var x = config.layers[i].x;
if(x > 0 && x < config.WIDTH)
snap_positions.x.push(x);
var x = config.layers[i].x + config.layers[i].width/2;
if(x > 0 && x < config.WIDTH)
snap_positions.x.push(x);
var x = config.layers[i].x + config.layers[i].width;
if(x > 0 && x < config.WIDTH)
snap_positions.x.push(x);
//y
var y = config.layers[i].y;
if(y > 0 && y < config.HEIGHT)
snap_positions.y.push(y);
var y = config.layers[i].y + config.layers[i].height/2;
if(y > 0 && y < config.HEIGHT)
snap_positions.y.push(y);
var y = config.layers[i].y + config.layers[i].height;
if(y > 0 && y < config.HEIGHT)
snap_positions.y.push(y);
}
return snap_positions;
}
/**
* calculates snap coordinates by current mouse position.
*
* @param event
* @param pos_x
* @param pos_y
* @param exclude_id
* @returns object|null
*/
calc_snap_position(event, pos_x, pos_y, exclude_id) {
var snap_position = { x: null, y: null };
var params = this.getParams();
if(config.SNAP === false || event.shiftKey == true || (event.ctrlKey == true || event.metaKey == true)){
this.snap_line_info = {x: null, y: null};
return null;
}
//settings
var sensitivity = 0.01;
var max_distance = (config.WIDTH + config.HEIGHT) / 2 * sensitivity / config.ZOOM;
//collect snap positions
if(typeof exclude_id != "undefined")
var snap_positions = this.get_snap_positions(exclude_id);
else
var snap_positions = this.get_snap_positions();
//find closest snap positions
var min_value = {
x: null,
y: null,
};
var min_distance = {
x: null,
y: null,
};
//x
for(var i in snap_positions.x){
var distance = Math.abs(pos_x - snap_positions.x[i]);
if(distance < max_distance && (distance < min_distance.x || min_distance.x === null)){
min_distance.x = distance;
min_value.x = snap_positions.x[i];
}
}
//y
for(var i in snap_positions.y){
var distance = Math.abs(pos_y - snap_positions.y[i]);
if(distance < max_distance && (distance < min_distance.y || min_distance.y === null)){
min_distance.y = distance;
min_value.y = snap_positions.y[i];
}
}
//apply snap
var success = false;
//x
if(min_value.x != null) {
snap_position.x = Math.round(min_value.x);
success = true;
this.snap_line_info.x = {
start_x: min_value.x,
start_y: 0,
end_x: min_value.x,
end_y: config.HEIGHT
};
}
else{
this.snap_line_info.x = null;
}
//y
if(min_value.y != null) {
snap_position.y = Math.round(min_value.y);
success = true;
this.snap_line_info.y = {
start_x: 0,
start_y: min_value.y,
end_x: config.WIDTH,
end_y: min_value.y,
};
}
else{
this.snap_line_info.y = null;
}
if(success) {
return snap_position;
}
return null;
}
}
export default Base_tools_class;
@@ -0,0 +1,184 @@
import Helper_class from './../../libs/helpers.js';
import Dialog_class from './../../libs/popup.js';
import GUI_colors_class from './../gui/gui-colors.js';
const Helper = new Helper_class();
/**
* This input opens a custom color picker dialog that is more tightly integrated with the application (swatch selection, etc).
* It can also handle alpha values, whereas native color input can't.
*/
(function ($) {
const template = `
<div class="ui_color_input" tabindex="-1">
<input type="color">
<div class="alpha_overlay"></div>
</div>
`;
const on_focus_color_input = (event) => {
const $el = $(event.target.closest('.ui_color_input'));
$el.trigger('focus');
};
const on_blur_color_input = (event) => {
const $el = $(event.target.closest('.ui_color_input'));
$el.trigger('blur');
};
const on_click_color_input = (event) => {
event.preventDefault();
const $el = $(event.target.closest('.ui_color_input'));
const { value } = $el.data();
const POP = new Dialog_class();
let colorsDialog = new GUI_colors_class();
var settings = {
title: 'Color Picker',
on_finish() {
set_value($el, colorsDialog.COLOR + (colorsDialog.ALPHA < 255 ? colorsDialog.ALPHA.toString(16).padStart(2, '0') : ''));
$el.trigger('input');
$el.trigger('change');
colorsDialog = null;
},
params: [
{
function() {
var html = '<div id="dialog_color_picker"></div>';
return html;
}
}
],
};
let colorValue;
let alpha = 255;
if (/^\#[0-9A-F]{8}$/gi.test(value)) {
// Hex with alpha
colorValue = value.slice(0, 7);
alpha = parseInt(value.slice(7, 9), 16);
} else if (/^\#[0-9A-F]{6}$/gi.test(value)) {
// Hex without alpha
colorValue = value;
} else {
colorValue = '#000000';
}
POP.show(settings);
colorsDialog.render_main_colors('dialog');
colorsDialog.set_color({ hex: colorValue, a: alpha });
};
const set_value = ($el, value) => {
const trimmedValue = (value + '').trim();
let colorValue;
let opacity = 0;
if (/^\#[0-9A-F]{8}$/gi.test(trimmedValue)) {
// Hex with alpha
colorValue = trimmedValue.slice(0, 7);
opacity = 1 - (parseInt(value.slice(7, 9), 16) * (1 / 255));
} else if (/^\#[0-9A-F]{6}$/gi.test(trimmedValue)) {
// Hex without alpha
colorValue = trimmedValue;
} else {
return;
}
const { input, overlay } = $el.data();
overlay.style.opacity = opacity;
input.value = colorValue;
$el.data('value', trimmedValue);
};
const set_disabled = ($el, disabled) => {
const { input } = $el.data();
if (disabled) {
input.setAttribute('disabled', 'disabled');
} else {
input.removeAttribute('disabled');
}
$el.data('disabled', disabled);
};
$.fn.uiColorInput = function(behavior, ...args) {
let returnValues = [];
for (let i = 0; i < this.length; i++) {
let el = this[i];
// Constructor
if (Object.prototype.toString.call(behavior) !== '[object String]') {
const definition = behavior || {};
const classList = el.className;
const id = definition.id != null ? definition.id : el.getAttribute('id');
const inputId = definition.inputId || '';
const disabled = definition.disabled != null ? definition.disabled : el.hasAttribute('disabled') ? true : false;
const value = definition.value != null ? definition.value : el.value || 0;
const ariaLabeledBy = el.getAttribute('aria-labelledby');
let $el;
if (el.parentNode) {
$(el).after(template);
const oldEl = el;
el = el.nextElementSibling;
$(oldEl).remove();
} else {
const orphanedParent = document.createElement('div');
orphanedParent.innerHTML = template;
el = orphanedParent.firstElementChild;
}
this[i] = el;
$el = $(el);
const input = $el.find('input[type="color"]')[0];
const overlay = $el.find('.alpha_overlay')[0];
if (classList) {
el.classList.add(classList);
}
if (id) {
el.setAttribute('id', id);
}
if (inputId) {
input.setAttribute('id', inputId);
}
if (ariaLabeledBy) {
input.setAttribute('aria-labelledby', ariaLabeledBy);
}
$el.data({
id,
input,
overlay,
value
});
$(input)
.on('click', on_click_color_input)
.on('focus', on_focus_color_input)
.on('blur', on_blur_color_input)
set_value($el, value);
set_disabled($el, disabled);
}
// Behaviors
else if (behavior === 'set_value') {
const newValue = args[0];
const $el = $(el);
if ($el.data('value') !== newValue) {
set_value($(el), newValue);
}
}
else if (behavior === 'get_value') {
returnValues.push($(el).data('value'));
}
else if (behavior === 'get_id') {
returnValues.push($(el).data('id'));
}
}
if (returnValues.length > 0) {
return returnValues.length === 1 ? returnValues[0] : returnValues;
} else {
return this;
}
}
})(jQuery);
@@ -0,0 +1,213 @@
import Helper_class from './../../libs/helpers.js';
var Helper = new Helper_class();
(function ($) {
const template = `
<div class="ui_color_picker_gradient">
<div class="secondary_pick" tabindex="0" role="figure" aria-label="Saturation vs value selection. Use left/right arrow keys to control saturation. Use up/down arrow keys to control value.">
<div class="saturation_gradient"></div>
<div class="value_gradient"></div>
<div class="handle"></div>
</div>
<div class="primary_pick">
<input aria-label="Hue" type="range" min="0" max="360" step="1" class="color_picker_thin" />
</div>
</div>
`;
const on_key_down_secondary_pick = (event) => {
const $el = $(event.target.closest('.ui_color_picker_gradient'));
const { hsv } = $el.data();
const key = event.key;
if (['Left', 'ArrowLeft'].includes(key)) {
event.preventDefault();
set_hsv($el, {
h: hsv.h,
s: hsv.s - 1/100,
v: hsv.v
});
$el.trigger('input');
}
else if (['Right', 'ArrowRight'].includes(key)) {
event.preventDefault();
set_hsv($el, {
h: hsv.h,
s: hsv.s + 1/100,
v: hsv.v
});
$el.trigger('input');
}
else if (['Up', 'ArrowUp'].includes(key)) {
event.preventDefault();
set_hsv($el, {
h: hsv.h,
s: hsv.s,
v: hsv.v + 1/100
});
$el.trigger('input');
}
else if (['Down', 'ArrowDown'].includes(key)) {
event.preventDefault();
set_hsv($el, {
h: hsv.h,
s: hsv.s,
v: hsv.v - 1/100
});
$el.trigger('input');
}
};
const on_mouse_down_secondary_pick = (event) => {
event.preventDefault();
const $el = $(event.target.closest('.ui_color_picker_gradient'));
const { secondaryPick, secondaryPickHandle, hsv } = $el.data();
const clientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
const clientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
const mouseDownSecondaryPickRect = secondaryPick.getBoundingClientRect();
const xRatio = (clientX - mouseDownSecondaryPickRect.left) / (mouseDownSecondaryPickRect.right - mouseDownSecondaryPickRect.left);
const yRatio = (clientY - mouseDownSecondaryPickRect.top) / (mouseDownSecondaryPickRect.bottom - mouseDownSecondaryPickRect.top);
set_hsv($el, {
h: hsv.h,
s: xRatio,
v: 1 - yRatio
});
$el.trigger('input');
$el.data({
mouseDownSecondaryPickRect,
mouseMoveWindowHandler: generate_on_mouse_move_window($el),
mouseUpWindowHandler: generate_on_mouse_up_window($el)
});
const $window = $(window);
$window.on('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
$window.on('mouseup touchend', $el.data('mouseUpWindowHandler'));
};
const on_touch_move_secondary_pick = (event) => {
event.preventDefault();
};
const generate_on_mouse_move_window = ($el) => {
return (event) => {
const { hsv, mouseDownSecondaryPickRect } = $el.data();
const clientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
const clientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
const xRatio = (clientX - mouseDownSecondaryPickRect.left) / (mouseDownSecondaryPickRect.right - mouseDownSecondaryPickRect.left);
const yRatio = (clientY - mouseDownSecondaryPickRect.top) / (mouseDownSecondaryPickRect.bottom - mouseDownSecondaryPickRect.top);
set_hsv($el, {
h: hsv.h,
s: xRatio,
v: 1 - yRatio
});
$el.trigger('input');
};
};
const generate_on_mouse_up_window = ($el) => {
return (event) => {
const $window = $(window);
$window.off('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
$window.off('mouseup touchend', $el.data('mouseUpWindowHandler'));
};
};
// All hsv values range from 0 to 1.
const set_hsv = ($el, hsv) => {
const { secondaryPick, secondaryPickHandle, primaryRange } = $el.data();
hsv.h = Math.max(0, Math.min(1, hsv.h));
hsv.s = Math.max(0, Math.min(1, hsv.s));
hsv.v = Math.max(0, Math.min(1, hsv.v));
$el.data('hsv', hsv);
$(primaryRange).uiRange('set_value', (1 - hsv.h) * 360);
secondaryPick.style.background = Helper.hsvToHex(hsv.h, 1, 1);
secondaryPickHandle.style.left = ((hsv.s) * 100) + '%';
secondaryPickHandle.style.top = ((1 - hsv.v) * 100) + '%';
};
$.fn.uiColorPickerGradient = function(behavior, ...args) {
let returnValues = [];
for (let i = 0; i < this.length; i++) {
let el = this[i];
// Constructor
if (Object.prototype.toString.call(behavior) !== '[object String]') {
const definition = behavior || {};
const id = definition.id != null ? definition.id : el.getAttribute('id');
const label = definition.label != null ? definition.label : el.getAttribute('aria-label');
const hsv = definition.hsv || { h: 0, s: 0, v: 0 };
$(el).after(template);
const oldEl = el;
el = el.nextElementSibling;
$(oldEl).remove();
this[i] = el;
if (id) {
el.setAttribute('id', id);
}
if (label) {
el.setAttribute('aria-label', label);
}
const $el = $(el);
const $primaryRange = $($el.find('.primary_pick input').get(0));
$primaryRange
.uiRange({ vertical: true })
.uiRange('set_background', 'linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%)')
.on('input', () => {
const { hsv } = $el.data();
set_hsv($el, {
h: 1 - ($primaryRange.uiRange('get_value') / 360),
s: hsv.s,
v: hsv.v
});
$el.trigger('input');
});
$el.find('> input').uiRange();
const secondaryPick = $el.find('.secondary_pick')[0];
$el.data({
primaryRange: $primaryRange[0],
secondaryPick,
secondaryPickHandle: $el.find('.secondary_pick .handle')[0],
hsv
});
set_hsv($el, hsv);
$(secondaryPick).on('keydown', on_key_down_secondary_pick);
$(secondaryPick).on('mousedown touchstart', on_mouse_down_secondary_pick);
$(secondaryPick).on('touchmove', on_touch_move_secondary_pick);
}
// Behaviors
else if (behavior === 'set_hsv') {
const $el = $(el);
const hsv = $el.data('hsv');
const newHsv = args[0];
if (newHsv && (hsv.h !== newHsv.h || hsv.s !== newHsv.s || hsv.v !== newHsv.v)) {
set_hsv($(el), newHsv);
}
}
else if (behavior === 'get_hsv') {
const hsv = $(el).data('hsv');
returnValues.push(JSON.parse(JSON.stringify(hsv)));
}
}
if (returnValues.length > 0) {
return returnValues.length === 1 ? returnValues[0] : returnValues;
} else {
return this;
}
};
})(jQuery);
@@ -0,0 +1,6 @@
import './color-input.js';
import './color-picker-gradient.js';
import './number-input.js';
import './range.js';
import './swatches.js';
@@ -0,0 +1,306 @@
import Helper_class from './../../libs/helpers.js';
var Helper = new Helper_class();
/**
* The purpose of using this class vs a native input[type="number"] is for custom styling and
* to allow for gestures on mobile that makes it easier to use with a thumb on a touch screen (future implementation)
*/
(function ($) {
const template = `
<div class="ui_number_input">
<input type="number">
<button class="increase_number" tabindex="-1"><span class="sr_only">Increase</span></button>
<button class="decrease_number" tabindex="-1"><span class="sr_only">Decrease</span></button>
</div>
`;
const on_focus_number_input = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
$el.trigger('focus', event);
};
const on_blur_number_input = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
$el.trigger('blur', event);
};
const on_input_number_input = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const value = $el.data('input').value;
if (value != '') {
set_value($el, $el.data('input').value);
}
$el.trigger('input', event);
};
const on_change_number_input = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { input, min } = $el.data();
let value = input.value;
if (value === '') {
value = 0;
}
set_value($el, value);
$el.trigger('change', event);
};
const on_wheel_number_input = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { value, step, disabled } = $el.data();
event.preventDefault();
const delta = (event.originalEvent.deltaY > 0 ? -1 : (event.originalEvent.deltaY < 0 ? 1 : 0));
if (!disabled && delta !== 0) {
set_value($el, (isNaN(value) ? 0 : value) + (step * delta)); // Intentionally not using get_step_amount
$el.trigger('input');
}
}
const on_touch_start_increase_button = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
if (!disabled) {
clearTimeout(buttonRepeatTimeout);
clearInterval(buttonRepeatInterval);
set_value($el, (isNaN(value) ? 0 : value) + get_step_amount($el, true));
$el.trigger('input');
}
};
const on_mouse_down_increase_button = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
if (!disabled) {
clearTimeout(buttonRepeatTimeout);
clearInterval(buttonRepeatInterval);
set_value($el, (isNaN(value) ? 0 : value) + get_step_amount($el, true));
$el.trigger('input');
$el.data('buttonRepeatTimeout', setTimeout(() => {
$el.data('buttonRepeatInterval', setInterval(() => {
const { value } = $el.data();
set_value($el, value + get_step_amount($el, true));
$el.trigger('input');
}, 50));
}, 400));
}
};
const on_mouse_up_increase_button = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { buttonRepeatTimeout, buttonRepeatInterval } = $el.data();
clearTimeout(buttonRepeatTimeout);
clearInterval(buttonRepeatInterval);
};
const on_touch_start_decrease_button = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
if (!disabled) {
clearTimeout(buttonRepeatTimeout);
clearInterval(buttonRepeatInterval);
set_value($el, (isNaN(value) ? 0 : value) - get_step_amount($el, false));
$el.trigger('input');
}
};
const on_mouse_down_decrease_button = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { value, buttonRepeatTimeout, buttonRepeatInterval, disabled } = $el.data();
if (!disabled) {
clearTimeout(buttonRepeatTimeout);
clearInterval(buttonRepeatInterval);
set_value($el, (isNaN(value) ? 0 : value) - get_step_amount($el, false));
$el.trigger('input');
$el.data('buttonRepeatTimeout', setTimeout(() => {
$el.data('buttonRepeatInterval', setInterval(() => {
const { value } = $el.data();
set_value($el, value - get_step_amount($el, false));
$el.trigger('input');
}, 50));
}, 400));
}
};
const on_mouse_up_decrease_button = (event) => {
const $el = $(event.target.closest('.ui_number_input'));
const { buttonRepeatTimeout, buttonRepeatInterval } = $el.data();
clearTimeout(buttonRepeatTimeout);
clearInterval(buttonRepeatInterval);
};
const set_value = ($el, value) => {
const { min, max, step, stepDecimalPlaces, input } = $el.data();
if (typeof value === 'string') {
value = parseFloat(value);
}
if (!isNaN(value)) {
value = parseFloat((step * Math.round(value / step)).toFixed(stepDecimalPlaces));
value = Math.max(min, Math.min(max, value));
if (value + '.' !== input.value) {
input.value = value;
}
} else {
value = parseFloat(null);
input.value = '';
}
$el.data('value', value);
};
const set_disabled = ($el, disabled) => {
const { input } = $el.data();
if (disabled) {
input.setAttribute('disabled', 'disabled');
} else {
input.removeAttribute('disabled');
}
$el.data('disabled', disabled);
};
const get_step_amount = ($el, increasing) => {
const { value, step, exponentialStepButtons } = $el.data();
if (exponentialStepButtons) {
let amount = step;
let absValue = Math.abs((isNaN(value) ? 0 : value));
if (absValue >= (increasing ? 500 : 501))
amount = 100;
else if (absValue >= (increasing ? 100 : 101))
amount = 50;
else if (absValue >= (increasing ? 10 : 11))
amount = 10;
else if (absValue >= (increasing ? 5 : 6))
amount = 5;
else
amount = 1;
return amount;
} else {
return step;
}
};
$.fn.uiNumberInput = function(behavior, ...args) {
let returnValues = [];
for (let i = 0; i < this.length; i++) {
let el = this[i];
// Constructor
if (Object.prototype.toString.call(behavior) !== '[object String]') {
const definition = behavior || {};
const classList = el.className;
const id = definition.id != null ? definition.id : el.getAttribute('id');
const min = definition.min != null ? definition.min : parseFloat(el.getAttribute('min')) || null;
const max = definition.max != null ? definition.max : parseFloat(el.getAttribute('max')) || null;
const step = definition.step != null ? definition.step : el.hasAttribute('step') ? parseFloat(el.getAttribute('step')) : 1;
const exponentialStepButtons = !!definition.exponentialStepButtons;
const disabled = definition.disabled != null ? definition.disabled : el.hasAttribute('disabled') ? true : false;
const value = definition.value != null ? definition.value : parseFloat(el.value) || 0;
const ariaLabeledBy = el.getAttribute('aria-labelledby');
let $el;
if (el.parentNode) {
$(el).after(template);
const oldEl = el;
el = el.nextElementSibling;
$(oldEl).remove();
} else {
const orphanedParent = document.createElement('div');
orphanedParent.innerHTML = template;
el = orphanedParent.firstElementChild;
}
this[i] = el;
$el = $(el);
const input = $el.find('input[type="number"]')[0];
const increaseButton = $el.find('.increase_number')[0];
const decreaseButton = $el.find('.decrease_number')[0];
if (classList) {
el.classList.add(classList);
}
if (id) {
el.setAttribute('id', id);
}
if (ariaLabeledBy) {
input.setAttribute('aria-labelledby', ariaLabeledBy);
}
if (min != null) {
input.setAttribute('min', min);
}
if (max != null) {
input.setAttribute('max', max);
}
if (Math.floor(step) === step) {
input.setAttribute('step', step);
} else {
input.setAttribute('step', 'any');
}
let stepDecimalPlaces = 0;
if ((step % 1) != 0)
stepDecimalPlaces = step.toString().split(".")[1].length;
$el.data({
id,
input,
increaseButton,
decreaseButton,
buttonRepeatTimeout: undefined,
buttonRepeatInterval: undefined,
value,
min,
max,
step,
stepDecimalPlaces,
exponentialStepButtons
});
$(input)
.on('focus', on_focus_number_input)
.on('blur', on_blur_number_input)
.on('input', on_input_number_input)
.on('change', on_change_number_input)
.on('wheel', on_wheel_number_input);
$(increaseButton)
.on('touchstart', on_touch_start_increase_button)
.on('mousedown', on_mouse_down_increase_button)
.on('mouseup mouseleave touchend', on_mouse_up_increase_button);
$(decreaseButton)
.on('touchstart', on_touch_start_decrease_button)
.on('mousedown', on_mouse_down_decrease_button)
.on('mouseup mouseleave', on_mouse_up_decrease_button);
set_value($el, value);
set_disabled($el, disabled);
}
// Behaviors
else if (behavior === 'set_value') {
const newValue = parseFloat(args[0]);
const $el = $(el);
if ($el.data('value') !== newValue) {
set_value($(el), newValue);
}
}
else if (behavior === 'get_value') {
returnValues.push($(el).data('value'));
}
else if (behavior === 'get_id') {
returnValues.push($(el).data('id'));
}
else if (behavior === 'set_disabled') {
const newValue = !!args[0];
set_disabled($(el), newValue);
}
else if (behavior === 'get_disabled') {
returnValues.push($(el).data('disabled'));
}
}
if (returnValues.length > 0) {
return returnValues.length === 1 ? returnValues[0] : returnValues;
} else {
return this;
}
};
})(jQuery);
@@ -0,0 +1,118 @@
/**
* ProviderBadge — compact status indicator in the left toolbar footer.
* Shows a dot + 3-5 char label; all details in the tooltip.
*/
import { getCapabilities } from '../../api/capabilities.js';
export async function mountProviderBadge(container) {
var caps = await getCapabilities();
var badge = document.createElement('div');
badge.id = 'provider-badge';
badge.style.cssText = [
'display:flex', 'flex-direction:column', 'align-items:center', 'gap:2px',
'padding:4px 2px 4px',
'font-size:9px', 'font-family:sans-serif', 'line-height:1.2',
'cursor:default', 'user-select:none',
'width:100%', 'box-sizing:border-box',
'text-align:center', 'word-break:break-word',
].join(';');
var dot = document.createElement('span');
dot.style.cssText = 'width:8px;height:8px;border-radius:50%;display:block;flex-shrink:0;';
var label = document.createElement('span');
label.style.cssText = 'color:inherit;max-width:36px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;';
var remote = caps.remote || {};
var local = caps.local || {};
if (remote.provider === 'local_gpu') {
var gpuName = _shortGpuName(local.gpu_device);
var tier = local.gpu_tier || '';
if (remote.healthy) {
dot.style.background = '#44cc44';
badge.style.color = '#aaffaa';
label.textContent = _shortTier(tier);
var flagList = [
local.gpu_fp16 && 'fp16',
local.gpu_bf16 && 'bf16',
local.gpu_fp8 && 'fp8',
local.gpu_tensor_cores && 'TC',
].filter(Boolean).join(' ');
badge.title = [
gpuName,
'VRAM: ' + local.gpu_vram_total + ' GB total / ' + local.gpu_vram_free + ' GB free',
'CC: ' + local.gpu_cc + ' Eff VRAM: ' + local.gpu_eff_vram + ' GB',
flagList ? 'Flags: ' + flagList : '',
tier ? 'Tier: ' + tier : '',
(local.local_gpu_warnings || []).length
? 'Warnings:\n' + local.local_gpu_warnings.join('\n')
: '',
].filter(Boolean).join('\n');
} else {
dot.style.background = '#ffaa00';
badge.style.color = '#ffdd88';
label.textContent = 'GPU?';
badge.title = 'local_gpu configured but diffusers may not be installed.\nCheck container logs.';
}
} else if (remote.provider && remote.healthy) {
dot.style.background = '#44cc44';
badge.style.color = '#aaffaa';
label.textContent = _shortProvider(remote.provider);
var opLines = Object.entries(remote.operations || {})
.map(([op, s]) => op + ': ' + (s.provider || remote.provider) + ' ' + (s.healthy ? '✓' : '✗'))
.join('\n');
badge.title = ('Provider: ' + remote.provider) + (opLines ? '\n' + opLines : '');
} else if (remote.provider && !remote.healthy) {
dot.style.background = '#ffaa00';
badge.style.color = '#ffdd88';
label.textContent = _shortProvider(remote.provider) + '?';
badge.title = remote.provider + ' configured but not reachable.\nCheck your .env URL.';
} else {
dot.style.background = '#888888';
badge.style.color = '#aaaaaa';
label.textContent = local.lama ? 'LaMa' : 'Local';
badge.title = 'Local only (no generative AI).\nSet AI_PROVIDER in .env to enable.';
}
badge.appendChild(dot);
badge.appendChild(label);
if (container) {
container.appendChild(badge);
}
return badge;
}
function _shortGpuName(name) {
return (name || 'GPU')
.replace(/^NVIDIA GeForce\s+/i, '')
.replace(/^NVIDIA Quadro\s+/i, '')
.replace(/^NVIDIA\s+/i, '')
.replace(/^AMD Radeon\s+/i, '');
}
function _shortTier(tier) {
if (!tier) return 'GPU';
// sdxl_offload → SDXL, flux → FLUX, sd15 → SD15
return tier
.replace(/_offload$/, '')
.replace(/_cpu$/, '')
.toUpperCase()
.slice(0, 6);
}
function _shortProvider(p) {
var map = {
openai: 'OAI', replicate: 'Rep', stability: 'Stab',
invokeai: 'Inv', comfyui: 'CUI', local_gpu: 'GPU',
};
return map[p] || (p || 'AI').slice(0, 4);
}
@@ -0,0 +1,234 @@
(function ($) {
const template = `
<div class="ui_range" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="1" aria-valuenow="0">
<div class="padded_track"></div>
<div class="bar">
<div class="handle"></div>
</div>
</div>
`;
const on_keydown_range = (event) => {
const $el = $(event.target.closest('.ui_range'));
const key = event.key;
const { value, step, min, max } = $el.data();
if (['Left', 'ArrowLeft', 'Down', 'ArrowDown'].includes(key)) {
event.preventDefault();
set_value($el, value - step);
$el.trigger('input');
}
else if (['Right', 'ArrowRight', 'Up', 'ArrowUp'].includes(key)) {
event.preventDefault();
set_value($el, value + step);
$el.trigger('input');
}
else if (['PageUp'].includes(key)) {
event.preventDefault();
set_value($el, value + (step * 10));
$el.trigger('input');
}
else if (['PageDown'].includes(key)) {
event.preventDefault();
set_value($el, value - (step * 10));
$el.trigger('input');
}
else if (['Home'].includes(key)) {
event.preventDefault();
set_value($el, min);
$el.trigger('input');
}
else if (['End'].includes(key)) {
event.preventDefault();
set_value($el, max);
$el.trigger('input');
}
};
const on_wheel_range = (event) => {
const $el = $(event.target.closest('.ui_range'));
if (document.activeElement === $el[0]) {
const { value, step } = $el.data();
if (event.originalEvent.deltaY < 0) {
event.preventDefault();
set_value($el, value + step);
$el.trigger('input');
}
else if (event.originalEvent.deltaY > 0) {
event.preventDefault();
set_value($el, value - step);
$el.trigger('input');
}
}
};
const on_mouse_down_range = (event) => {
event.preventDefault();
const target = event.touches && event.touches.length > 0 ? event.touches[0].target : event.target;
const $el = $(target.closest('.ui_range'));
const { handle, paddedTrack, value, min, max, vertical } = $el.data();
const mouseDownClientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
const mouseDownClientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
const mouseDownPaddedTrackRect = paddedTrack.getBoundingClientRect();
let mouseDownValue = value;
if (target !== handle) {
let range, valueInRange;
if (vertical) {
range = mouseDownPaddedTrackRect.top - mouseDownPaddedTrackRect.bottom;
valueInRange = mouseDownClientY - mouseDownPaddedTrackRect.bottom;
} else {
range = mouseDownPaddedTrackRect.right - mouseDownPaddedTrackRect.left;
valueInRange = mouseDownClientX - mouseDownPaddedTrackRect.left;
}
const ratio = Math.max(0, Math.min(1, valueInRange / range));
mouseDownValue = (max - min) * ratio;
set_value($el, mouseDownValue);
$el.trigger('input');
}
$el.data({
mouseDownValue,
mouseDownClientX,
mouseDownClientY,
mouseDownPaddedTrackRect,
mouseMoveWindowHandler: generate_on_mouse_move_window($el),
mouseUpWindowHandler: generate_on_mouse_up_window($el)
});
$el.addClass('active');
const $window = $(window);
$window.on('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
$window.on('mouseup touchend', $el.data('mouseUpWindowHandler'));
$el[0].focus();
};
const on_touch_move_range = (event) => {
event.preventDefault();
};
const generate_on_mouse_move_window = ($el) => {
return (event) => {
event.preventDefault();
event.stopPropagation();
const { mouseDownValue, min, max, vertical, mouseDownClientX, mouseDownClientY, mouseDownPaddedTrackRect } = $el.data();
let range, offset, startValue;
if (vertical) {
const clientY = event.touches && event.touches.length > 0 ? event.touches[0].clientY : event.clientY;
range = mouseDownPaddedTrackRect.top - mouseDownPaddedTrackRect.bottom;
const mouseDownValueInPixelRange = ((mouseDownValue - min) / (max - min)) * range;
startValue = mouseDownClientY - mouseDownPaddedTrackRect.bottom;
offset = clientY - mouseDownClientY + (mouseDownValueInPixelRange - startValue);
} else {
const clientX = event.touches && event.touches.length > 0 ? event.touches[0].clientX : event.clientX;
range = mouseDownPaddedTrackRect.right - mouseDownPaddedTrackRect.left;
const mouseDownValueInPixelRange = ((mouseDownValue - min) / (max - min)) * range;
startValue = mouseDownClientX - mouseDownPaddedTrackRect.left;
offset = clientX - mouseDownClientX + (mouseDownValueInPixelRange - startValue);
}
const ratio = Math.max(0, Math.min(1, (startValue + offset) / range));
const value = (max - min) * ratio;
set_value($el, value);
$el.trigger('input');
};
};
const generate_on_mouse_up_window = ($el) => {
return (event) => {
const $window = $(window);
$el.removeClass('active');
$window.off('mousemove touchmove', $el.data('mouseMoveWindowHandler'));
$window.off('mouseup touchend', $el.data('mouseUpWindowHandler'));
};
};
const set_value = ($el, value) => {
const { bar, min, max, step, vertical } = $el.data();
value = step * Math.round(value / step);
value = Math.max(min, Math.min(max, value));
$el.data('value', value);
$el.attr('aria-valuemin', min);
$el.attr('aria-valuemax', max);
$el.attr('aria-valuenow', value);
if (vertical) {
bar.style.height = (((value - min) / (max - min)) * 100) + '%';
} else {
bar.style.width = (((value - min) / (max - min)) * 100) + '%';
}
};
$.fn.uiRange = function(behavior, ...args) {
let returnValues = [];
for (let i = 0; i < this.length; i++) {
let el = this[i];
// Constructor
if (Object.prototype.toString.call(behavior) !== '[object String]') {
const definition = behavior || {};
const classList = el.className;
const id = definition.id != null ? definition.id : el.getAttribute('id');
const value = definition.value != null ? definition.value : parseFloat(el.value) || 0;
const min = definition.min != null ? definition.min : parseFloat(el.getAttribute('min')) || 0;
const max = definition.max != null ? definition.max : parseFloat(el.getAttribute('max')) || 0;
const step = definition.step != null ? definition.step : el.hasAttribute('step') ? parseFloat(el.getAttribute('step')) : 1;
const vertical = !!definition.vertical;
$(el).after(template);
const oldEl = el;
el = el.nextElementSibling;
$(oldEl).remove();
this[i] = el;
const $el = $(el);
if (classList) {
el.classList.add(classList);
}
if (vertical) {
el.classList.add('vertical');
}
if (id) {
el.setAttribute('id', id);
}
$el.data({
paddedTrack: $('.padded_track', el).get(0),
bar: $('.bar', el).get(0),
handle: $('.handle', el).get(0),
vertical,
value,
min,
max,
step
});
set_value($el, value);
$el
.on('mousedown touchstart', on_mouse_down_range)
.on('touchmove', on_touch_move_range)
.on('keydown', on_keydown_range)
.on('wheel', on_wheel_range);
}
// Behaviors
else if (behavior === 'set_background') {
const backgroundStyle = args[0];
$(el).data('paddedTrack').style.background = backgroundStyle;
}
else if (behavior === 'set_value') {
const newValue = parseFloat(args[0]);
const $el = $(el);
if ($el.data('value') !== newValue) {
set_value($(el), newValue);
}
}
else if (behavior === 'get_value') {
returnValues.push($(el).data('value'));
}
}
if (returnValues.length > 0) {
return returnValues.length === 1 ? returnValues[0] : returnValues;
} else {
return this;
}
};
})(jQuery);
@@ -0,0 +1,170 @@
(function ($) {
const template = `
<div class="ui_swatches">
<div class="swatch_group" tabindex="0">
</div>
</div>
`;
const on_key_down_swatches = (event) => {
const $el = $(event.target.closest('.ui_swatches'));
const key = event.key;
const { rows, count, selectedIndex } = $el.data();
if (['Left', 'ArrowLeft'].includes(key)) {
event.preventDefault();
set_selected_index($el, selectedIndex - 1);
$el.trigger('input');
}
else if (['Right', 'ArrowRight'].includes(key)) {
event.preventDefault();
set_selected_index($el, selectedIndex + 1);
$el.trigger('input');
}
else if (['Up', 'ArrowUp'].includes(key)) {
event.preventDefault();
set_selected_index($el, selectedIndex - Math.floor(count / rows));
$el.trigger('input');
}
else if (['Down', 'ArrowDown'].includes(key)) {
event.preventDefault();
set_selected_index($el, selectedIndex + Math.floor(count / rows));
$el.trigger('input');
}
};
const on_click_swatches = (event) => {
const target = event.target;
const $el = $(target.closest('.ui_swatches'));
if (target.classList.contains('swatch')) {
const { swatches } = $el.data();
set_selected_index($el, swatches.indexOf(target));
$el.trigger('input');
}
};
const set_selected_index = ($el, index) => {
const { readonly, swatches } = $el.data();
if (swatches[index]) {
$el.data('selectedIndex', index);
if (!readonly) {
$el.find('.active').removeClass('active');
$(swatches[index]).addClass('active');
}
}
};
const set_selected_hex = ($el, hex) => {
const { selectedIndex, swatches } = $el.data();
if (/^\#[0-9A-F]{6}$/gi.test(hex)) {
const swatch = swatches[selectedIndex];
$(swatch)
.data('hex', hex)
.css('background-color', hex);
}
};
const set_all_hex = ($el, hexArray) => {
hexArray = hexArray || [];
const { swatches } = $el.data();
for (let i = 0; i < swatches.length; i++) {
if (hexArray[i]) {
const hex = hexArray[i];
if (/^\#[0-9A-F]{6}$/gi.test(hex)) {
$(swatches[i])
.data('hex', hex)
.css('background-color', hex);
}
} else {
break;
}
}
}
$.fn.uiSwatches = function(behavior, ...args) {
let returnValues = [];
for (let i = 0; i < this.length; i++) {
let el = this[i];
// Constructor
if (Object.prototype.toString.call(behavior) !== '[object String]') {
const definition = behavior || {};
const id = definition.id != null ? definition.id : el.getAttribute('id');
const cols = definition.cols;
const rows = definition.rows || 1;
const count = definition.count || 10;
const readonly = definition.readonly || false;
const selectedIndex = definition.selectedIndex != null ? definition.selectedIndex : 0;
$(el).after(template);
const oldEl = el;
el = el.nextElementSibling;
$(oldEl).remove();
this[i] = el;
const $el = $(el);
const swatchGroup = $el.find('.swatch_group')[0];
if (id) {
el.setAttribute('id', id);
}
if (cols) {
swatchGroup.classList.add('cols_' + cols);
}
swatchGroup.classList.add('rows_' + rows);
const swatches = [];
for (let i = 0; i < count; i++) {
const swatch = document.createElement('div');
swatch.classList.add('swatch');
$(swatch).data('hex', '#ffffff');
swatches.push(swatch);
swatchGroup.appendChild(swatch);
if (i === selectedIndex && !readonly) {
swatch.classList.add('active');
}
}
$el.data({
selectedIndex,
swatchGroup,
swatches,
count,
cols,
rows,
readonly
});
$el
.on('click', on_click_swatches)
.on('keydown', on_key_down_swatches);
}
// Behaviors
else if (behavior === 'set_selected_hex') {
const newValue = args[0] + '';
set_selected_hex($(el), newValue);
}
else if (behavior === 'get_selected_hex') {
const { selectedIndex, swatches } = $(el).data();
returnValues.push($(swatches[selectedIndex]).data('hex'));
}
else if (behavior === 'set_all_hex') {
set_all_hex($(el), args[0]);
}
else if (behavior === 'get_all_hex') {
const { swatches } = $(el).data();
for (let swatch of swatches) {
returnValues.push($(swatch).data('hex'));
}
}
}
if (returnValues.length > 0) {
return returnValues.length === 1 ? returnValues[0] : returnValues;
} else {
return this;
}
};
})(jQuery);
@@ -0,0 +1,587 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../../config.js';
import Helper_class from './../../libs/helpers.js';
import Tools_translate_class from './../../modules/tools/translate.js';
const Helper = new Helper_class();
const sidebarTemplate = `
<div class="ui_flex_group justify_content_space_between stacked">
<div id="selected_color_sample" class="ui_color_sample" title="Current Color Preview"></div>
<div class="ui_button_group">
<button id="toggle_color_picker_section_button" aria-pressed="true" class="ui_icon_button trn" title="Toggle Color Picker">
<span class="sr_only">Toggle Color Picker</span>
<svg width="1em" height="1em" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<rect width="24" height="24" opacity="0" />
<path d="M19.54 5.08A10.61 10.61 0 0 0 11.91 2a10 10 0 0 0-.05 20 2.58 2.58 0 0 0 2.53-1.89 2.52 2.52 0 0 0-.57-2.28.5.5 0 0 1 .37-.83h1.65A6.15 6.15 0 0 0 22 11.33a8.48 8.48 0 0 0-2.46-6.25zM15.88 15h-1.65a2.49 2.49 0 0 0-1.87 4.15.49.49 0 0 1 .12.49c-.05.21-.28.34-.59.36a8 8 0 0 1-7.82-9.11A8.1 8.1 0 0 1 11.92 4H12a8.47 8.47 0 0 1 6.1 2.48 6.5 6.5 0 0 1 1.9 4.77A4.17 4.17 0 0 1 15.88 15z" />
<circle cx="12" cy="6.5" r="1.5" />
<path d="M15.25 7.2a1.5 1.5 0 1 0 2.05.55 1.5 1.5 0 0 0-2.05-.55z" />
<path d="M8.75 7.2a1.5 1.5 0 1 0 .55 2.05 1.5 1.5 0 0 0-.55-2.05z" />
<path d="M6.16 11.26a1.5 1.5 0 1 0 2.08.4 1.49 1.49 0 0 0-2.08-.4z" />
</svg>
</button>
<button id="toggle_color_channels_section_button" aria-pressed="true" class="ui_icon_button trn" title="Toggle Color Channels">
<span class="sr_only">Toggle Color Channels</span>
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-card-list" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M14.5 3h-13a.5.5 0 0 0-.5.5v9a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5zm-13-1A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-13z"/>
<path fill-rule="evenodd" d="M5 8a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7A.5.5 0 0 1 5 8zm0-2.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5z"/>
<circle cx="3.5" cy="5.5" r=".5"/>
<circle cx="3.5" cy="8" r=".5"/>
<circle cx="3.5" cy="10.5" r=".5"/>
</svg>
</button>
<button id="toggle_color_swatches_section_button" aria-pressed="true" class="ui_icon_button trn" title="Toggle Swatches">
<span class="sr_only">Toggle Swatches</span>
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-grid-3x2" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M0 3.5A1.5 1.5 0 0 1 1.5 2h13A1.5 1.5 0 0 1 16 3.5v8a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 11.5v-8zM1.5 3a.5.5 0 0 0-.5.5V7h4V3H1.5zM5 8H1v3.5a.5.5 0 0 0 .5.5H5V8zm1 0h4v4H6V8zm4-1H6V3h4v4zm1 1v4h3.5a.5.5 0 0 0 .5-.5V8h-4zm0-1V3h3.5a.5.5 0 0 1 .5.5V7h-4z"/>
</svg>
</button>
</div>
</div>
<div id="color_section_swatches" class="block_section">
<div id="color_swatches"></div>
</div>
<div id="color_section_picker" class="block_section">
<input id="color_picker_gradient" type="color" aria-label="Color Selection">
<div class="ui_input_group stacked">
<label id="color_hex_label" title="Hex" class="label_width_small trn">Hex</label>
<input id="color_hex" aria-labelledby="color_hex_label" value="#000000" maxlength="7" type="text" />
</div>
</div>
<div id="color_section_channels" class="block_section color_section_channels">
<div class="ui_input_grid stacked">
<div class="ui_input_group">
<label id="rgb_r_label" title="Red" class="label_width_character text_red"><strong>R<span class="sr_only">ed</span></strong></label>
<input id="rgb_r_range" aria-labelledby="rgb_r_label" type="range" min="0" max="255" class="color_picker" />
<input id="rgb_r" min="0" aria-labelledby="rgb_r_label" max="255" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="rgb_g_label" title="Green" class="label_width_character text_green"><strong>G<span class="sr_only">reen</span></strong></label>
<input id="rgb_g_range" aria-labelledby="rgb_g_label" type="range" min="0" max="255" class="color_picker" />
<input id="rgb_g" min="0" aria-labelledby="rgb_g_label" max="255" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="rgb_b_label" title="Blue" class="label_width_character text_blue"><strong>B<span class="sr_only">lue</span></strong></label>
<input id="rgb_b_range" aria-labelledby="rgb_b_label" type="range" min="0" max="255" class="color_picker" />
<input id="rgb_b" min="0" aria-labelledby="rgb_b_label" max="255" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="rgb_a_label" title="Alpha" class="label_width_character text_muted"><strong>A<span class="sr_only">lpha</span></strong></label>
<input id="rgb_a_range" aria-labelledby="rgb_a_label" type="range" min="0" max="255" class="color_picker" />
<input id="rgb_a" min="0" aria-labelledby="rgb_a_label" max="255" type="number" class="input_cw_3" />
</div>
</div>
<div class="ui_input_grid stacked">
<div class="ui_input_group">
<label id="hsl_h_label" title="Hue" class="label_width_character"><strong>H<span class="sr_only">ue</span></strong></label>
<input id="hsl_h_range" aria-labelledby="hsl_h_label" type="range" min="0" max="360" class="color_picker" />
<input id="hsl_h" min="0" aria-labelledby="hsl_h_label" max="360" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="hsl_s_label" title="Saturation" class="label_width_character"><strong>S<span class="sr_only">aturation</span></strong></label>
<input id="hsl_s_range" aria-labelledby="hsl_s_label" type="range" min="0" max="100" class="color_picker" />
<input id="hsl_s" min="0" aria-labelledby="hsl_s_label"max="100" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="hsl_l_label" title="Luminosity" class="label_width_character"><strong>L<span class="sr_only">uminosity</span></strong></label>
<input id="hsl_l_range" aria-labelledby="hsl_l_label" type="range" min="0" max="100" class="color_picker" />
<input id="hsl_l" min="0" aria-labelledby="hsl_l_label"max="100" type="number" class="input_cw_3" />
</div>
</div>
</div>
`;
const dialogTemplate = `
<div class="ui_flex_group">
<div id="dialog_color_picker_group" class="ui_flex_group column">
<input id="dialog_color_picker_gradient" type="color" aria-label="Color Selection">
<div class="block_section">
<div class="ui_input_grid stacked">
<div class="ui_input_group">
<label class="label_width_medium trn">Current</label>
<div id="dialog_selected_color_sample" class="ui_color_sample"></div>
</div>
<div class="ui_input_group">
<label class="label_width_medium trn">Previous</label>
<div id="dialog_previous_color_sample" class="ui_color_sample"></div>
</div>
</div>
</div>
</div>
<div id="dialog_color_channel_group">
<div class="ui_input_group stacked">
<label id="dialog_color_hex_label" title="Hex" class="label_width_small trn">Hex</label>
<input id="dialog_color_hex" aria-labelledby="dialog_color_hex_label" value="#000000" maxlength="7" type="text" />
</div>
<div class="ui_input_grid stacked">
<div class="ui_input_group">
<label id="dialog_rgb_r_label" title="Red" class="label_width_character text_red"><strong>R<span class="sr_only">ed</span></strong></label>
<input id="dialog_rgb_r_range" aria-labelledby="dialog_rgb_r_label" type="range" min="0" max="255" class="color_picker" />
<input id="dialog_rgb_r" min="0" aria-labelledby="dialog_rgb_r_label" max="255" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="dialog_rgb_g_label" title="Green" class="label_width_character text_green"><strong>G<span class="sr_only">reen</span></strong></label>
<input id="dialog_rgb_g_range" aria-labelledby="dialog_rgb_g_label" type="range" min="0" max="255" class="color_picker" />
<input id="dialog_rgb_g" min="0" aria-labelledby="dialog_rgb_g_label" max="255" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="dialog_rgb_b_label" title="Blue" class="label_width_character text_blue"><strong>B<span class="sr_only">lue</span></strong></label>
<input id="dialog_rgb_b_range" aria-labelledby="dialog_rgb_b_label" type="range" min="0" max="255" class="color_picker" />
<input id="dialog_rgb_b" min="0" aria-labelledby="dialog_rgb_b_label" max="255" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="dialog_rgb_a_label" title="Alpha" class="label_width_character text_muted"><strong>A<span class="sr_only">lpha</span></strong></label>
<input id="dialog_rgb_a_range" aria-labelledby="dialog_rgb_a_label" type="range" min="0" max="255" class="color_picker" />
<input id="dialog_rgb_a" min="0" aria-labelledby="dialog_rgb_a_label" max="255" type="number" class="input_cw_3" />
</div>
</div>
<div class="ui_input_grid stacked">
<div class="ui_input_group">
<label id="dialog_hsl_h_label" title="Hue" class="label_width_character"><strong>H<span class="sr_only">ue</span></strong></label>
<input id="dialog_hsl_h_range" aria-labelledby="dialog_hsl_h_label" type="range" min="0" max="360" class="color_picker" />
<input id="dialog_hsl_h" min="0" aria-labelledby="dialog_hsl_h_label" max="360" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="dialog_hsl_s_label" title="Saturation" class="label_width_character"><strong>S<span class="sr_only">aturation</span></strong></label>
<input id="dialog_hsl_s_range" aria-labelledby="dialog_hsl_s_label" type="range" min="0" max="100" class="color_picker" />
<input id="dialog_hsl_s" min="0" aria-labelledby="dialog_hsl_s_label"max="100" type="number" class="input_cw_3" />
</div>
<div class="ui_input_group">
<label id="dialog_hsl_l_label" title="Luminosity" class="label_width_character"><strong>L<span class="sr_only">uminosity</span></strong></label>
<input id="dialog_hsl_l_range" aria-labelledby="dialog_hsl_l_label" type="range" min="0" max="100" class="color_picker" />
<input id="dialog_hsl_l" min="0" aria-labelledby="dialog_hsl_l_label"max="100" type="number" class="input_cw_3" />
</div>
</div>
<div class="block_section">
<div id="dialog_color_swatches"></div>
</div>
</div>
</div>
`;
/**
* GUI class responsible for rendering colors block on right sidebar
*/
class GUI_colors_class {
constructor() {
this.el = null;
this.COLOR = '#000000';
this.ALPHA = 255;
this.colorNotSet = true;
this.uiType = null;
this.butons = null;
this.sections = null;
this.inputs = null;
this.Helper = new Helper_class();
this.Tools_translate = new Tools_translate_class();
}
render_main_colors(uiType) {
this.uiType = uiType || 'sidebar';
if (this.uiType === 'dialog') {
this.el = document.getElementById('dialog_color_picker');
this.el.innerHTML = dialogTemplate;
} else {
var saved_color = this.Helper.getCookie('color');
if (saved_color != null) config.COLOR = saved_color;
this.el = document.getElementById('toggle_colors');
this.el.innerHTML = sidebarTemplate;
}
if (config.LANG != 'en') {
this.Tools_translate.translate(config.LANG, this.el);
}
this.init_components();
this.render_ui_deferred = Helper.throttle(this.render_ui_deferred, 50);
}
init_components() {
// Store button references
this.buttons = {
toggleColorSwatches: $('#toggle_color_swatches_section_button', this.el),
toggleColorPicker: $('#toggle_color_picker_section_button', this.el),
toggleColorChannels: $('#toggle_color_channels_section_button', this.el)
};
// Store UI section references
this.sections = {
swatches: $('#color_section_swatches', this.el),
swatchesPlaceholder: document.createComment('Placeholder comment for color swatches'),
picker: $('#color_section_picker', this.el),
pickerPlaceholder: document.createComment('Placeholder comment for color picker'),
channels: $('#color_section_channels', this.el),
channelsPlaceholder: document.createComment('Placeholder comment for color channels')
};
// Store references to all inputs in DOM
const idPrefix = this.uiType === 'dialog' ? 'dialog_' : '';
this.inputs = {
sample: $(`#${idPrefix}selected_color_sample`, this.el),
swatches: $(`#${idPrefix}color_swatches`, this.el),
pickerGradient: $(`#${idPrefix}color_picker_gradient`, this.el),
hex: $(`#${idPrefix}color_hex`, this.el),
rgb: {
r: {
range: $(`#${idPrefix}rgb_r_range`, this.el),
number: $(`#${idPrefix}rgb_r`, this.el)
},
g: {
range: $(`#${idPrefix}rgb_g_range`, this.el),
number: $(`#${idPrefix}rgb_g`, this.el)
},
b: {
range: $(`#${idPrefix}rgb_b_range`, this.el),
number: $(`#${idPrefix}rgb_b`, this.el)
},
a: {
range: $(`#${idPrefix}rgb_a_range`, this.el),
number: $(`#${idPrefix}rgb_a`, this.el)
}
},
hsl: {
h: {
range: $(`#${idPrefix}hsl_h_range`, this.el),
number: $(`#${idPrefix}hsl_h`, this.el)
},
s: {
range: $(`#${idPrefix}hsl_s_range`, this.el),
number: $(`#${idPrefix}hsl_s`, this.el)
},
l: {
range: $(`#${idPrefix}hsl_l_range`, this.el),
number: $(`#${idPrefix}hsl_l`, this.el)
}
}
};
// Handle toggle for color swatches section
this.buttons.toggleColorSwatches
.on('click', () => {
this.buttons.toggleColorSwatches.attr('aria-pressed', 'true' === this.buttons.toggleColorSwatches.attr('aria-pressed') ? 'false' : 'true');
const isPressed = this.buttons.toggleColorSwatches.attr('aria-pressed') === 'true';
if (isPressed) {
this.sections.swatchesPlaceholder.parentNode.insertBefore(this.sections.swatches[0], this.sections.swatchesPlaceholder.nextSibling);
this.sections.swatchesPlaceholder.parentNode.removeChild(this.sections.swatchesPlaceholder);
} else {
this.sections.swatches[0].parentNode.insertBefore(this.sections.swatchesPlaceholder, this.sections.swatches[0].nextSibling);
this.sections.swatches[0].parentNode.removeChild(this.sections.swatches[0]);
}
Helper.setCookie('toggle_color_swatches', isPressed ? 1 : 0);
});
// Restore toggle preference, default to hidden for swatches
const saved_toggle_color_swatches = Helper.getCookie('toggle_color_swatches');
if (saved_toggle_color_swatches === 0 || saved_toggle_color_swatches == null) {
this.buttons.toggleColorSwatches.trigger('click');
}
// Handle toggle for color picker section
this.buttons.toggleColorPicker
.on('click', () => {
this.buttons.toggleColorPicker.attr('aria-pressed', 'true' === this.buttons.toggleColorPicker.attr('aria-pressed') ? 'false' : 'true');
const isPressed = this.buttons.toggleColorPicker.attr('aria-pressed') === 'true';
if (isPressed) {
this.sections.pickerPlaceholder.parentNode.insertBefore(this.sections.picker[0], this.sections.pickerPlaceholder.nextSibling);
this.sections.pickerPlaceholder.parentNode.removeChild(this.sections.pickerPlaceholder);
} else {
this.sections.picker[0].parentNode.insertBefore(this.sections.pickerPlaceholder, this.sections.picker[0].nextSibling);
this.sections.picker[0].parentNode.removeChild(this.sections.picker[0]);
}
Helper.setCookie('toggle_color_picker', isPressed ? 1 : 0);
});
this.inputs.sample.on('click', (event) => {
this.buttons.toggleColorPicker.click();
});
// Restore toggle preference, default to visible for picker
const saved_toggle_color_picker = Helper.getCookie('toggle_color_picker');
if (saved_toggle_color_picker === 0) {
this.buttons.toggleColorPicker.trigger('click');
}
// Handle toggle for color channels section
this.buttons.toggleColorChannels
.on('click', () => {
this.buttons.toggleColorChannels.attr('aria-pressed', 'true' === this.buttons.toggleColorChannels.attr('aria-pressed') ? 'false' : 'true');
const isPressed = this.buttons.toggleColorChannels.attr('aria-pressed') === 'true';
if (isPressed) {
this.sections.channelsPlaceholder.parentNode.insertBefore(this.sections.channels[0], this.sections.channelsPlaceholder.nextSibling);
this.sections.channelsPlaceholder.parentNode.removeChild(this.sections.channelsPlaceholder);
} else {
this.sections.channels[0].parentNode.insertBefore(this.sections.channelsPlaceholder, this.sections.channels[0].nextSibling);
this.sections.channels[0].parentNode.removeChild(this.sections.channels[0]);
}
Helper.setCookie('toggle_color_channels', isPressed ? 1 : 0);
});
// Restore toggle preference, default to hidden for swatches
const saved_toggle_color_channels = Helper.getCookie('toggle_color_channels');
if (saved_toggle_color_channels === 0 || saved_toggle_color_channels == null) {
this.buttons.toggleColorChannels.trigger('click');
}
// Initialize color swatches
this.inputs.swatches
.uiSwatches({ rows: 3, cols: 7, count: 21, readonly: this.uiType === 'dialog' })
.on('input', () => {
this.set_color({
hex: this.inputs.swatches.uiSwatches('get_selected_hex')
});
});
if (this.uiType === 'dialog') {
this.inputs.swatches.uiSwatches('set_all_hex', config.swatches.default);
}
// Initialize color picker gradient
this.inputs.pickerGradient
.uiColorPickerGradient()
.on('input', () => {
const hsv = this.inputs.pickerGradient.uiColorPickerGradient('get_hsv');
this.set_color({
h: hsv.h * 360,
s: hsv.s * 100,
v: hsv.v * 100
});
});
// Initialize hex entry
this.inputs.hex
.on('input', (event) => {
const value = this.inputs.hex.val();
const trimmedValue = value.trim();
if (value !== trimmedValue) {
this.inputs.hex.val(trimmedValue);
}
this.inputs.hex[0].setCustomValidity(/^\#[0-9A-F]{6}$/gi.test(trimmedValue) ? '' : 'Invalid Hex Code');
this.set_color({ hex: this.inputs.hex.val() });
})
.on('blur', () => {
const value = this.inputs.hex.val();
if (!/^\#[0-9A-F]{6}$/gi.test(value)) {
this.inputs.hex.val(this.uiType === 'dialog' ? this.COLOR : config.COLOR);
this.inputs.hex[0].setCustomValidity('');
}
});
// Initialize the color sliders
const sliderInputs = [
...Object.entries(this.inputs.rgb),
...Object.entries(this.inputs.hsl)
];
for (const [key, input] of sliderInputs) {
input.range && input.range
.uiRange()
.on('input', () => {
this.set_color({ [key]: input.range.uiRange('get_value') });
});
input.number && input.number
.uiNumberInput()
.on('input', () => {
this.set_color({ [key]: input.number.uiNumberInput('get_value') });
})
}
// Update all inputs from config.COLOR
this.render_selected_color();
}
/**
* Changes the config.COLOR variable based on the given input.
* @param {*} definition object contains the value of the color to change:
* hex - set the color as a hex code
* r,g,b - set the color as red, green, blue values [0-255]
* a - set the color alpha [0-255]
* h,s,l - set the color as hue [0-360], saturation [0-100], luminosity [0-100]
* h,s,v - set the color as hue [0-360], saturation [0-100], value [0-100]
*/
set_color(definition) {
let newColor = null;
let newAlpha = null;
let hsl = null;
let hsv = null;
// Set new color by hex code
if ('hex' in definition) {
const hex = '#' + definition.hex.replace(/[^0-9A-F]*/gi, '');
if (/^\#[0-9A-F]{6}$/gi.test(hex)) {
newColor = '#' + definition.hex.trim().replace(/^\#/, '');
}
}
// Set new color by rgb
else if ('r' in definition || 'b' in definition || 'g' in definition) {
const previousRgb = Helper.hexToRgb(this.uiType === 'dialog' ? this.COLOR : config.COLOR);
newColor = Helper.rgbToHex(
'r' in definition ? Math.min(255, Math.max(0, parseInt(definition.r, 10) || 0)) : previousRgb.r,
'g' in definition ? Math.min(255, Math.max(0, parseInt(definition.g, 10) || 0)) : previousRgb.g,
'b' in definition ? Math.min(255, Math.max(0, parseInt(definition.b, 10) || 0)) : previousRgb.b
);
}
// Set new color by hsv
else if ('v' in definition) {
const previousRgb = Helper.hexToRgb(this.uiType === 'dialog' ? this.COLOR : config.COLOR);
const previousHsv = Helper.rgbToHsv(previousRgb.r, previousRgb.g, previousRgb.b);
hsv = {
h: 'h' in definition ? Math.min(360, Math.max(0, parseInt(definition.h, 10) || 0)) / 360 : previousHsv.h,
s: 's' in definition ? Math.min(100, Math.max(0, parseInt(definition.s, 10) || 0)) / 100 : previousHsv.s,
v: 'v' in definition ? Math.min(100, Math.max(0, parseInt(definition.v, 10) || 0)) / 100 : previousHsv.v
};
newColor = Helper.hsvToHex(hsv.h, hsv.s, hsv.v);
}
// Set new color by hsl
else if ('h' in definition || 's' in definition || 'l' in definition) {
hsl = {
h: ('h' in definition ? Math.min(360, Math.max(0, parseInt(definition.h, 10) || 0)) : parseInt(this.inputs.hsl.h.number.uiNumberInput('get_value'), 10)) / 360,
s: ('s' in definition ? Math.min(100, Math.max(0, parseInt(definition.s, 10) || 0)) : parseInt(this.inputs.hsl.s.number.uiNumberInput('get_value'), 10)) / 100,
l: ('l' in definition ? Math.min(100, Math.max(0, parseInt(definition.l, 10) || 0)) : parseInt(this.inputs.hsl.l.number.uiNumberInput('get_value'), 10)) / 100
};
newColor = Helper.hslToHex(hsl.h, hsl.s, hsl.l);
}
// Set new alpha
if ('a' in definition) {
newAlpha = Math.min(255, Math.max(0, parseInt(Math.ceil(definition.a), 10)));
}
// Re-render UI if changes made
if (newColor != null || newAlpha != null) {
if (this.uiType === 'dialog') {
this.COLOR = newColor != null ? newColor : this.COLOR;
this.ALPHA = newAlpha != null ? newAlpha : this.ALPHA;
if (this.colorNotSet) {
this.colorNotSet = false;
$('#dialog_previous_color_sample', this.el)[0].style.background = this.COLOR;
}
} else {
config.COLOR = newColor != null ? newColor : config.COLOR;
config.ALPHA = newAlpha != null ? newAlpha : config.ALPHA;
}
if (hsl && !hsv) {
hsv = Helper.hslToHsv(hsl.h, hsl.s, hsl.l);
}
if (hsv && !hsl) {
hsl = Helper.hsvToHsl(hsv.h, hsv.s, hsv.v);
}
this.render_selected_color({ hsl, hsv });
}
if (this.uiType === 'sidebar') {
this.Helper.setCookie('color', config.COLOR);
}
}
/**
* Renders current color defined in the config to all color fields
* @param {*} options additional options:
* hsl - override for hsl values so it isn't calculated based on rgb (can lose selected hue/saturation otherwise)
* hsv - override for hsv values so it isn't calculated based on rgb (can lose selected hue/saturation otherwise)
*/
render_selected_color(options) {
options = options || {};
const COLOR = this.uiType === 'dialog' ? this.COLOR : config.COLOR;
const ALPHA = this.uiType === 'dialog' ? this.ALPHA : config.ALPHA;
this.inputs.sample.css('background', COLOR);
if (this.uiType !== 'dialog') {
this.inputs.swatches.uiSwatches('set_selected_hex', COLOR);
}
const hexInput = this.inputs.hex[0];
hexInput.value = COLOR;
hexInput.setCustomValidity('');
const rgb = Helper.hexToRgb(COLOR);
delete rgb.a;
for (let rgbKey in rgb) {
this.inputs.rgb[rgbKey].range.uiRange('set_value', rgb[rgbKey]);
this.inputs.rgb[rgbKey].number.uiNumberInput('set_value', rgb[rgbKey]);
}
this.inputs.rgb.a.range.uiRange('set_value', ALPHA);
this.inputs.rgb.a.number.uiNumberInput('set_value', ALPHA);
const hsv = options.hsv || Helper.rgbToHsv(rgb.r, rgb.g, rgb.b);
const hsl = options.hsl || Helper.rgbToHsl(rgb.r, rgb.g, rgb.b);
for (let hslKey in hsl) {
const hslValue = Math.round(hsl[hslKey] * (hslKey === 'h' ? 360 : 100));
this.inputs.hsl[hslKey].range.uiRange('set_value', hslValue);
this.inputs.hsl[hslKey].number.uiNumberInput('set_value', hslValue);
}
this.render_ui_deferred({ hsl, hsv });
}
/**
* Renders the color gradients in each channel's color range selection.
* This function is throttled due to expensive operations on low-end systems.
* @param {*} options additional options:
* hsl - override for hsl values so it isn't calculated based on rgb (can lose selected hue/saturation otherwise)
* hsv - override for hsv values so it isn't calculated based on rgb (can lose selected hue/saturation otherwise)
*/
render_ui_deferred(options) {
options = options || {};
const COLOR = this.uiType === 'dialog' ? this.COLOR : config.COLOR;
// RGB
const rgb = Helper.hexToRgb(COLOR);
delete rgb.a;
for (let rgbKey in rgb) {
const rangeMin = JSON.parse(JSON.stringify(rgb));
const rangeMax = JSON.parse(JSON.stringify(rgb));
rangeMin[rgbKey] = 0;
rangeMax[rgbKey] = 255;
this.inputs.rgb[rgbKey].range.uiRange('set_background',
`linear-gradient(to right, ${ Helper.rgbToHex(rangeMin.r, rangeMin.g, rangeMin.b) }, ${ Helper.rgbToHex(rangeMax.r, rangeMax.g, rangeMax.b) })`
);
}
// A
this.inputs.rgb.a.range.uiRange('set_background',
`linear-gradient(to right, transparent, ${ COLOR })`
);
// HSV
const hsv = options.hsv || Helper.rgbToHsv(rgb.r, rgb.g, rgb.b);
this.inputs.pickerGradient.uiColorPickerGradient('set_hsv', hsv);
// HSL
const hsl = options.hsl || Helper.rgbToHsl(rgb.r, rgb.g, rgb.b);
// HSL - H
this.inputs.hsl.h.range.uiRange('set_background',
`linear-gradient(to right, ${
Helper.hex_set_hsl('#ff0000', { s: hsl.s, l: hsl.l })
} 0%, ${
Helper.hex_set_hsl('#ffff00', { s: hsl.s, l: hsl.l })
} 17%, ${
Helper.hex_set_hsl('#00ff00', { s: hsl.s, l: hsl.l })
} 33%, ${
Helper.hex_set_hsl('#00ffff', { s: hsl.s, l: hsl.l })
} 50%, ${
Helper.hex_set_hsl('#0000ff', { s: hsl.s, l: hsl.l })
} 67%, ${
Helper.hex_set_hsl('#ff00ff', { s: hsl.s, l: hsl.l })
} 83%, ${
Helper.hex_set_hsl('#ff0000', { s: hsl.s, l: hsl.l })
} 100%)`
);
// HSL - S
let rangeMin = JSON.parse(JSON.stringify(hsl));
let rangeMax = JSON.parse(JSON.stringify(hsl));
rangeMin.s = 0;
rangeMax.s = 1;
this.inputs.hsl.s.range.uiRange('set_background',
`linear-gradient(to right, ${ Helper.hslToHex(rangeMin.h, rangeMin.s, rangeMin.l) }, ${ Helper.hslToHex(rangeMax.h, rangeMax.s, rangeMax.l) })`
);
// HSL - L
let rangeMid = JSON.parse(JSON.stringify(hsl));
rangeMid.l = 0.5;
this.inputs.hsl.l.range.uiRange('set_background',
`linear-gradient(to right, #000000 0%, ${ Helper.hslToHex(rangeMid.h, rangeMid.s, rangeMid.l) } 50%, #ffffff 100%)`
);
// Store swatch values
if (this.uiType === 'sidebar') {
config.swatches.default = this.inputs.swatches.uiSwatches('get_all_hex');
}
}
}
export default GUI_colors_class;
@@ -0,0 +1,786 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import app from './../../app.js';
import config from './../../config.js';
import Dialog_class from './../../libs/popup.js';
import Text_class from './../../tools/text.js';
import Base_layers_class from "../base-layers";
import Tools_settings_class from './../../modules/tools/settings.js';
import Helper_class from './../../libs/helpers.js';
import Tools_translate_class from './../../modules/tools/translate.js';
var template = `
<div class="row">
<span class="trn label">X</span>
<input type="number" id="detail_x" step="any" />
<button class="extra reset trn" type="button" id="reset_x" title="Reset">Reset</button>
</div>
<div class="row">
<span class="trn label">Y:</span>
<input type="number" id="detail_y" step="any" />
<button class="extra reset trn" type="button" id="reset_y" title="Reset">Reset</button>
</div>
<div class="row">
<span class="trn label">Width:</span>
<input type="number" id="detail_width" step="any" />
<button class="extra reset trn" type="button" id="reset_size" title="Reset">Reset</button>
</div>
<div class="row">
<span class="trn label">Height:</span>
<input type="number" id="detail_height" step="any" />
<button class="extra trn" type="button" id="toggle_aspect_lock" title="Lock Aspect Ratio" style="font-size:16px;">&#128279;</button>
</div>
<hr />
<div class="row">
<span class="trn label">Rotate:</span>
<input type="number" min="-360" max="360" id="detail_rotate" />
<button class="extra reset trn" type="button" id="reset_rotate" title="Reset">Reset</button>
</div>
<div class="row">
<span class="trn label">Opacity:</span>
<input type="number" min="0" max="100" id="detail_opacity" />
<button class="extra reset trn" type="button" id="reset_opacity" title="Reset">Reset</button>
</div>
<div class="row">
<span class="trn label">Color:</span>
<input style="padding: 0px;" type="color" id="detail_color" />
</div>
<div id="parameters_container"></div>
<div id="text_detail_params">
<div class="row center">
<span class="trn label">&nbsp;</span>
<button type="button" class="trn dots" id="detail_param_text">Edit text...</button>
</div>
<div class="row">
<span class="trn label" title="Resize Boundary">Bounds:</span>
<select id="detail_param_boundary">
<option value="box">Box</option>
<option value="dynamic">Dynamic</option>
</select>
</div>
<div class="row">
<span class="trn label" title="Auto Kerning">Kerning:</span>
<select id="detail_param_kerning">
<option value="none">None</option>
<option value="metrics">Metrics</option>
</select>
</div>
<div class="row" hidden> <!-- Future implementation -->
<span class="trn label">Direction:</span>
<select id="detail_param_text_direction">
<option value="ltr">Left to Right</option>
<option value="rtl">Right to Left</option>
<option value="ttb">Top to Bottom</option>
<option value="btt">Bottom to Top</option>
</select>
</div>
<div class="row" hidden> <!-- Future implementation -->
<span class="trn label">Wrap:</span>
<select id="detail_param_wrap_direction">
<option value="ltr">Left to Right</option>
<option value="rtl">Right to Left</option>
<option value="ttb">Top to Bottom</option>
<option value="btt">Bottom to Top</option>
</select>
</div>
<div class="row">
<span class="trn label">Wrap At:</span>
<select id="detail_param_wrap">
<option value="letter">Word + Letter</option>
<option value="word">Word</option>
</select>
</div>
<div class="row">
<span class="trn label" title="Horizontal Alignment">H. Align:</span>
<select id="detail_param_halign">
<option value="left">Left</option>
<option value="center">Center</option>
<option value="right">Right</option>
</select>
</div>
<div class="row" hidden> <!-- Future implementation -->
<span class="trn label" title="Vertical Alignment">V. Align:</span>
<select id="detail_param_valign">
<option value="top">Top</option>
<option value="middle">Middle</option>
<option value="bottom">Bottom</option>
</select>
</div>
<div>
`;
/**
* GUI class responsible for rendering selected layer details block on right sidebar
*/
class GUI_details_class {
constructor() {
this.POP = new Dialog_class();
this.Text = new Text_class();
this.Base_layers = new Base_layers_class();
this.Tools_settings = new Tools_settings_class();
this.Helper = new Helper_class();
this.layer_details_active = false;
this.Tools_translate = new Tools_translate_class();
this.aspect_locked = true; // Default to locked for image layers
this.aspect_ratio = 1; // Will be calculated from layer dimensions
}
render_main_details() {
document.getElementById('toggle_details').innerHTML = template;
if (config.LANG != 'en') {
this.Tools_translate.translate(config.LANG, document.getElementById('toggle_details'));
}
this.render_details(true);
}
render_details(events = false) {
this.render_general('x', events);
this.render_general('y', events);
this.render_general('width', events);
this.render_general('height', events);
this.render_aspect_lock(events);
this.render_general('rotate', events);
this.render_general('opacity', events);
this.render_color(events);
this.render_reset(events);
//text - special case
if (config.layer != undefined && config.layer.type == 'text') {
document.getElementById('text_detail_params').style.display = 'block';
document.getElementById('detail_color').closest('.row').style.display = 'none';
}
else{
document.getElementById('text_detail_params').style.display = 'none';
if (config.layer != undefined && (config.layer.color === null || config.layer.type == 'image')) {
//hide color
document.getElementById('detail_color').closest('.row').style.display = 'none';
}
else {
//show color
document.getElementById('detail_color').closest('.row').style.display = 'block';
}
}
//add params
this.render_more_parameters();
this.render_text(events);
this.render_general_select_param('boundary', events);
this.render_general_select_param('kerning', events);
this.render_general_select_param('text_direction', events);
this.render_general_select_param('wrap', events);
this.render_general_select_param('wrap_direction', events);
this.render_general_select_param('halign', events);
this.render_general_select_param('valign', events);
}
render_general(key, events) {
var layer = config.layer;
var _this = this;
var units = this.Tools_settings.get_setting('default_units');
var resolution = this.Tools_settings.get_setting('resolution');
if (layer != undefined) {
var target = document.getElementById('detail_' + key);
target.dataset.layer = layer.id;
if (layer[key] == null) {
target.value = '';
target.disabled = true;
}
else {
var value = layer[key];
if(key == 'x' || key == 'y' || key == 'width' || key == 'height'){
//convert units
value = this.Helper.get_user_unit(value, units, resolution);
}
else {
value = Math.round(value);
}
//set
target.value = value;
target.disabled = false;
}
}
if (events) {
//events
var target = document.getElementById('detail_' + key);
if(target == undefined){
console.log('Error: missing details event target ' + 'detail_' + key);
return;
}
let focus_value = null;
target.addEventListener('focus', function (e) {
focus_value = parseFloat(this.value);
});
target.addEventListener('blur', function (e) {
if(key == 'x' || key == 'y' || key == 'width' || key == 'height'){
//convert units
var value = _this.Helper.get_internal_unit(this.value, units, resolution);
}
else {
var value = parseInt(this.value);
}
var layer = _this.Base_layers.get_layer(e.target.dataset.layer);
layer[key] = focus_value;
if (focus_value !== value) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(layer.id, {
[key]: value
})
])
);
}
});
target.addEventListener('change', function (e) {
if(key == 'x' || key == 'y' || key == 'width' || key == 'height'){
//convert units
var value = _this.Helper.get_internal_unit(this.value, units, resolution);
}
else {
var value = parseInt(this.value);
}
if(this.min != undefined && this.min != '' && value < this.min){
document.getElementById('detail_opacity').value = value;
value = this.min;
}
if(this.max != undefined && this.min != '' && value > this.max){
document.getElementById('detail_opacity').value = value;
value = this.max;
}
config.layer[key] = value;
config.need_render = true;
});
target.addEventListener('keyup', function (e) {
//for edge....
if (e.keyCode != 13) {
return;
}
if(key == 'x' || key == 'y' || key == 'width' || key == 'height'){
//convert units
var value = _this.Helper.get_internal_unit(this.value, units, resolution);
}
else {
var value = parseInt(this.value);
}
if(this.min != undefined && this.min != '' && value < this.min){
document.getElementById('detail_opacity').value = value;
value = this.min;
}
if(this.max != undefined && this.min != '' && value > this.max){
document.getElementById('detail_opacity').value = value;
value = this.max;
}
config.layer[key] = value;
config.need_render = true;
});
}
}
render_aspect_lock(events) {
var _this = this;
var layer = config.layer;
var lockBtn = document.getElementById('toggle_aspect_lock');
if (!lockBtn) return;
// Update aspect ratio from current layer dimensions
if (layer && layer.width && layer.height) {
this.aspect_ratio = layer.width / layer.height;
}
// Update button appearance based on lock state
if (this.aspect_locked) {
lockBtn.style.background = '#4a4';
lockBtn.title = 'Aspect Ratio Locked - Click to Unlock';
} else {
lockBtn.style.background = '';
lockBtn.title = 'Aspect Ratio Unlocked - Click to Lock';
}
if (events) {
lockBtn.addEventListener('click', function() {
_this.aspect_locked = !_this.aspect_locked;
// Update aspect ratio when locking
if (_this.aspect_locked && config.layer) {
_this.aspect_ratio = config.layer.width / config.layer.height;
}
_this.render_aspect_lock(false);
});
// Override width change to update height when locked
var widthInput = document.getElementById('detail_width');
var heightInput = document.getElementById('detail_height');
widthInput.addEventListener('input', function(e) {
if (_this.aspect_locked && config.layer) {
var units = _this.Tools_settings.get_setting('default_units');
var resolution = _this.Tools_settings.get_setting('resolution');
var newWidth = _this.Helper.get_internal_unit(this.value, units, resolution);
var newHeight = newWidth / _this.aspect_ratio;
heightInput.value = _this.Helper.get_user_unit(newHeight, units, resolution);
config.layer.height = newHeight;
}
});
heightInput.addEventListener('input', function(e) {
if (_this.aspect_locked && config.layer) {
var units = _this.Tools_settings.get_setting('default_units');
var resolution = _this.Tools_settings.get_setting('resolution');
var newHeight = _this.Helper.get_internal_unit(this.value, units, resolution);
var newWidth = newHeight * _this.aspect_ratio;
widthInput.value = _this.Helper.get_user_unit(newWidth, units, resolution);
config.layer.width = newWidth;
}
});
}
}
render_general_param(key, events) {
var layer = config.layer;
if (layer != undefined) {
var target = document.getElementById('detail_param_' + key);
if (layer.params[key] == null) {
target.value = '';
target.disabled = true;
}
else {
if (typeof layer.params[key] == 'boolean') {
//boolean
if(target.tagName == 'BUTTON'){
if(layer.params[key]){
target.classList.add('active');
}
else{
target.classList.remove('active');
}
}
}
else {
//common
target.value = layer.params[key];
}
target.disabled = false;
}
}
if (events) {
//events
var target = document.getElementById('detail_param_' + key);
let focus_value = null;
target.addEventListener('focus', function (e) {
focus_value = parseInt(this.value);
});
target.addEventListener('blur', function (e) {
var value = parseInt(this.value);
config.layer.params[key] = focus_value;
let params_copy = JSON.parse(JSON.stringify(config.layer.params));
params_copy[key] = value;
if (focus_value !== value) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
params: params_copy
})
])
);
}
});
target.addEventListener('change', function (e) {
var value = parseInt(this.value);
config.layer.params[key] = value;
config.need_render = true;
config.need_render_changed_params = true;
});
target.addEventListener('click', function (e) {
if (typeof config.layer.params[key] != 'boolean')
return;
this.classList.toggle('active');
config.layer.params[key] = !config.layer.params[key];
config.need_render = true;
config.need_render_changed_params = true;
});
}
}
render_general_select_param(key, events){
var layer = config.layer;
if (layer != undefined) {
var target = document.getElementById('detail_param_' + key);
if (layer.params[key] == null) {
target.value = '';
target.disabled = true;
}
else {
if(typeof layer.params[key] == 'object')
target.value = layer.params[key].value; //legacy
else
target.value = layer.params[key];
target.disabled = false;
}
}
if (events) {
//events
var target = document.getElementById('detail_param_' + key);
let focus_value = null;
target.addEventListener('focus', function (e) {
focus_value = this.value;
});
target.addEventListener('blur', function (e) {
var value = this.value;
config.layer.params[key] = focus_value;
let params_copy = JSON.parse(JSON.stringify(config.layer.params));
params_copy[key] = value;
if (focus_value !== value) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
params: params_copy
})
])
);
}
});
target.addEventListener('change', function (e) {
var value = this.value;
config.layer.params[key] = value;
config.need_render = true;
config.need_render_changed_params = true;
});
}
}
/**
* item: color
*/
render_color(events) {
var layer = config.layer;
let $colorInput;
if (events) {
$colorInput = $(document.getElementById('detail_color')).uiColorInput();
} else {
$colorInput = $(document.getElementById('detail_color'));
}
if (layer != undefined) {
$colorInput.uiColorInput('set_value', layer.color);
}
if (events) {
//events
let focus_value = null;
$colorInput.on('focus', function (e) {
focus_value = $colorInput.uiColorInput('get_value');
});
$colorInput.on('change', function (e) {
const value = $colorInput.uiColorInput('get_value');
config.layer.color = focus_value;
if (focus_value !== value) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
color: value
})
])
);
}
});
}
}
/**
* item: size reset button
*/
render_reset(events) {
var layer = config.layer;
if (layer != undefined) {
//size
if (layer.width_original != null) {
document.getElementById('reset_size').classList.remove('hidden');
}
else {
document.getElementById('reset_size').classList.add('hidden');
}
}
if (events) {
//events
document.getElementById('reset_x').addEventListener('click', function (e) {
if (config.layer.x) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
x: 0
})
])
);
}
});
document.getElementById('reset_y').addEventListener('click', function (e) {
if (config.layer.y) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
y: 0
})
])
);
}
});
document.getElementById('reset_size').addEventListener('click', function (e) {
if (config.layer.width !== config.layer.width_original
|| config.layer.height !== config.layer.height_original) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
width: config.layer.width_original,
height: config.layer.height_original
})
])
);
}
});
document.getElementById('reset_rotate').addEventListener('click', function (e) {
if (config.layer.rotate) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
rotate: 0
})
])
);
}
});
document.getElementById('reset_opacity').addEventListener('click', function (e) {
if (config.layer.opacity != 100) {
app.State.do_action(
new app.Actions.Bundle_action('change_layer_details', 'Change Layer Details', [
new app.Actions.Update_layer_action(config.layer.id, {
opacity: 100
})
])
);
}
});
}
}
/**
* item: text
*/
render_text(events) {
if (events) {
//events
document.getElementById('detail_param_text').addEventListener('click', function (e) {
document.querySelector('#tools_container #text').click();
document.getElementById('text_tool_keyboard_input').focus();
config.need_render = true;
});
}
}
render_more_parameters() {
var _this = this;
var target_id = "parameters_container";
const itemContainer = document.getElementById(target_id);
if(this.layer_details_active == true){
return;
}
itemContainer.innerHTML = "";
if(!config.layer || typeof config.layer.params == 'undefined' || config.layer.type == 'text') {
return;
}
//find layer parameters settings
var params_config = null;
for (var i in config.TOOLS) {
if (config.TOOLS[i].name == config.layer.type) {
params_config = config.TOOLS[i];
}
}
if(params_config == null){
return;
}
for (var k in params_config.attributes) {
var item = params_config.attributes[k];
//hide some fields, in future name should start with underscore
if(params_config.name == 'rectangle' && k == 'square'
|| params_config.name == 'ellipse' && k == 'circle'
|| params_config.name == 'pencil' && k == 'pressure'
|| params_config.name == 'pencil' && k == 'size'){
continue;
}
//row
let item_row = document.createElement('div');
item_row.className = 'row';
itemContainer.appendChild(item_row);
//title
var title = k[0].toUpperCase() + k.slice(1);
title = title.replace("_", " ");
let item_title = document.createElement('span');
item_title.className = 'trn label';
item_title.innerHTML = title;
item_row.appendChild(item_title);
//value
if (typeof item == 'boolean' || (typeof item == 'object' && typeof item.value == 'boolean')) {
//boolean - true, false
const elementInput = document.createElement('button');
elementInput.type = 'button';
elementInput.className = 'trn ui_toggle_button';
elementInput.innerHTML = title;
elementInput.dataset.key = k;
item_row.appendChild(elementInput);
let value = config.layer.params[k];
elementInput.setAttribute('aria-pressed', value);
//events
elementInput.addEventListener('click', function (e) {
//on leave
let layer = config.layer;
let key = this.dataset.key;
let new_value = elementInput.getAttribute('aria-pressed') !== 'true';
let params = JSON.parse(JSON.stringify(config.layer.params));
params[key] = new_value;
app.State.do_action(
new app.Actions.Update_layer_action(layer.id, {
params: params
})
);
});
}
else if (typeof item == 'number' || (typeof item == 'object' && typeof item.value == 'number')) {
//numbers
const elementInput = document.createElement('input');
elementInput.type = 'number';
elementInput.dataset.key = k;
item_row.appendChild(elementInput);
let min = 1;
let max = k === 'power' ? 100 : 999;
let step = null;
let value = config.layer.params[k];
if (typeof item == 'object') {
value = item.value;
if (item.min != null) {
min = item.min;
}
if (item.max != null) {
max = item.max;
}
if (item.step != null) {
step = item.step;
}
}
elementInput.setAttribute('min', min);
elementInput.setAttribute('max', max);
if (item.step != null) {
elementInput.setAttribute('step', step);
}
elementInput.setAttribute('value', config.layer.params[k]);
//events
let focus_value = null;
elementInput.addEventListener('focus', function (e) {
focus_value = parseFloat(this.value);
_this.layer_details_active = true;
});
elementInput.addEventListener('blur', function (e) {
//on leave
_this.layer_details_active = false;
let layer = config.layer;
let key = this.dataset.key;
let new_value = parseInt(this.value);
let params = JSON.parse(JSON.stringify(config.layer.params));
params[key] = new_value;
if (focus_value !== new_value) {
app.State.do_action(
new app.Actions.Update_layer_action(layer.id, {
params: params
})
);
}
});
elementInput.addEventListener('change', function (e) {
//on change - lots of events here in short time
let key = this.dataset.key;
let new_value = parseInt(this.value);
config.layer.params[key] = new_value;
config.need_render = true;
});
}
else if (typeof item == 'string' && item[0] == '#') {
//color
var elementInput = document.createElement('input');
elementInput.type = 'color';
let focus_value = null;
const $colorInput = $(elementInput).uiColorInput({
id: k,
value: item
})
.on('change', () => {
let layer = config.layer;
let key = $colorInput.uiColorInput('get_id');
let new_value = $colorInput.uiColorInput('get_value');
let params = JSON.parse(JSON.stringify(config.layer.params));
params[key] = new_value;
app.State.do_action(
new app.Actions.Update_layer_action(layer.id, {
params: params
})
);
});
$colorInput.uiColorInput('set_value', config.layer.params[k]);
item_row.appendChild($colorInput[0]);
}
else {
alertify.error('Error: unsupported attribute type:' + typeof item + ', ' + k);
}
}
}
}
export default GUI_details_class;
@@ -0,0 +1,105 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../../config.js';
import Base_layers_class from './../base-layers.js';
import Tools_settings_class from './../../modules/tools/settings.js';
import Helper_class from './../../libs/helpers.js';
import Tools_translate_class from './../../modules/tools/translate.js';
var template = `
<span class="trn label">Size:</span>
<span id="mouse_info_size">-</span>
<span class="id-mouse_info_units"></span>
<br />
<span class="trn label">Mouse:</span>
<span id="mouse_info_mouse">-</span>
<span class="id-mouse_info_units"></span>
<br />
<span class="trn label">Resolution:</span>
<span id="mouse_info_resolution">-</span>
`;
/**
* GUI class responsible for rendering information block on right sidebar
*/
class GUI_information_class {
constructor(ctx) {
this.Base_layers = new Base_layers_class();
this.Tools_settings = new Tools_settings_class();
this.Helper = new Helper_class();
this.Tools_translate = new Tools_translate_class();
this.last_width = null;
this.last_height = null;
this.units = this.Tools_settings.get_setting('default_units');
this.resolution = this.Tools_settings.get_setting('resolution');
}
render_main_information() {
document.getElementById('toggle_info').innerHTML = template;
if (config.LANG != 'en') {
this.Tools_translate.translate(config.LANG, document.getElementById('toggle_info'));
}
this.set_events();
this.show_size();
}
set_events() {
var _this = this;
var target = document.getElementById('mouse_info_mouse');
//show width and height
//should use canvas resize API in future
document.addEventListener('mousemove', function (e) {
_this.show_size();
}, false);
//show current mouse position
document.getElementById('canvas_minipaint').addEventListener('mousemove', function (e) {
var global_pos = _this.Base_layers.get_world_coords(e.offsetX, e.offsetY);
var mouse_x = Math.ceil(global_pos.x);
var mouse_y = Math.ceil(global_pos.y);
mouse_x = _this.Helper.get_user_unit(mouse_x, _this.units, _this.resolution);
mouse_y = _this.Helper.get_user_unit(mouse_y, _this.units, _this.resolution);
target.innerHTML = mouse_x + ', ' + mouse_y;
}, false);
}
update_units(){
this.units = this.Tools_settings.get_setting('default_units');
this.resolution = this.Tools_settings.get_setting('resolution');
this.show_size(true);
}
show_size(force) {
if(force == undefined && this.last_width == config.WIDTH && this.last_height == config.HEIGHT) {
return;
}
var width = this.Helper.get_user_unit(config.WIDTH, this.units, this.resolution);
var height = this.Helper.get_user_unit(config.HEIGHT, this.units, this.resolution);
document.getElementById('mouse_info_size').innerHTML = width + ' x ' + height;
var resolution = this.Tools_settings.get_setting('resolution');
document.getElementById('mouse_info_resolution').innerHTML = resolution;
//show units
var default_units = this.Tools_settings.get_setting('default_units_short');
var targets = document.querySelectorAll('.id-mouse_info_units');
for (var i = 0; i < targets.length; i++) {
targets[i].innerHTML = default_units;
}
this.last_width = config.WIDTH;
this.last_height = config.HEIGHT;
}
}
export default GUI_information_class;
@@ -0,0 +1,341 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import app from './../../app.js';
import config from './../../config.js';
import Base_layers_class from './../base-layers.js';
import Helper_class from './../../libs/helpers.js';
import Layer_rename_class from './../../modules/layer/rename.js';
import Effects_browser_class from './../../modules/effects/browser.js';
import Layer_duplicate_class from './../../modules/layer/duplicate.js';
import Layer_raster_class from './../../modules/layer/raster.js';
import Layer_scale_class from './../../modules/layer/scale.js';
import Layer_merge_class from './../../modules/layer/merge.js';
import Layer_flatten_class from './../../modules/layer/flatten.js';
import Tools_translate_class from './../../modules/tools/translate.js';
var template = `
<button type="button" class="layer_add trn" id="insert_layer" title="Insert new layer">+</button>
<button type="button" class="layer_duplicate trn" id="layer_duplicate" title="Duplicate layer">D</button>
<button type="button" class="layer_raster trn" id="layer_raster" title="Convert layer to raster">R</button>
<button type="button" class="layer_scale trn" id="layer_scale" title="Scale layer">S</button>
<button type="button" class="layers_arrow trn" title="Move layer down" id="layer_down">&darr;</button>
<button type="button" class="layers_arrow trn" title="Move layer up" id="layer_up">&uarr;</button>
<div class="layers_list" id="layers"></div>
<div class="layer_context_menu" id="layer_context_menu"></div>
`;
/**
* GUI class responsible for rendering layers on right sidebar
*/
class GUI_layers_class {
constructor(ctx) {
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.Layer_rename = new Layer_rename_class();
this.Effects_browser = new Effects_browser_class();
this.Layer_duplicate = new Layer_duplicate_class();
this.Layer_raster = new Layer_raster_class();
this.Layer_scale = new Layer_scale_class();
this.Layer_merge = new Layer_merge_class();
this.Layer_flatten = new Layer_flatten_class();
this.Tools_translate = new Tools_translate_class();
this.contextMenuLayerId = null;
}
render_main_layers() {
document.getElementById('layers_base').innerHTML = template;
if (config.LANG != 'en') {
this.Tools_translate.translate(config.LANG, document.getElementById('layers_base'));
}
this.render_layers();
this.set_events();
}
set_events() {
var _this = this;
document.getElementById('layers_base').addEventListener('click', function (event) {
var target = event.target;
if (target.id == 'insert_layer') {
//new layer
app.State.do_action(
new app.Actions.Insert_layer_action()
);
}
else if (target.id == 'layer_duplicate') {
//duplicate
_this.Layer_duplicate.duplicate();
}
else if (target.id == 'layer_raster') {
//raster
_this.Layer_raster.raster();
}
else if (target.id == 'layer_scale') {
//scale
_this.Layer_scale.scale();
}
else if (target.id == 'layer_up') {
//move layer up
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, 1)
);
}
else if (target.id == 'layer_down') {
//move layer down
app.State.do_action(
new app.Actions.Reorder_layer_action(config.layer.id, -1)
);
}
else if (target.id == 'visibility') {
//change visibility
return app.State.do_action(
new app.Actions.Toggle_layer_visibility_action(target.dataset.id)
);
}
else if (target.id == 'delete') {
//delete layer
app.State.do_action(
new app.Actions.Delete_layer_action(target.dataset.id)
);
}
else if (target.id == 'layer_name') {
//select layer
if (target.dataset.id == config.layer.id)
return;
app.State.do_action(
new app.Actions.Select_layer_action(target.dataset.id)
);
}
else if (target.id == 'delete_filter') {
//delete filter
app.State.do_action(
new app.Actions.Delete_layer_filter_action(target.dataset.pid, target.dataset.id)
);
}
else if (target.id == 'filter_name') {
//edit filter
var effects = _this.Effects_browser.get_effects_list();
var key = target.dataset.filter.toLowerCase();
for (var i in effects) {
if(effects[i].title.toLowerCase() == key){
_this.Base_layers.select(target.dataset.pid);
var function_name = _this.Effects_browser.get_function_from_path(key);
effects[i].object[function_name](target.dataset.id);
}
}
}
});
document.getElementById('layers_base').addEventListener('dblclick', function (event) {
var target = event.target;
if (target.id == 'layer_name') {
//rename layer
_this.Layer_rename.rename(target.dataset.id);
}
});
// Right-click context menu for layers
document.getElementById('layers_base').addEventListener('contextmenu', function (event) {
var target = event.target;
// Check if right-clicked on a layer item
if (target.id == 'layer_name' || target.closest('.item')) {
event.preventDefault();
var layerId = target.dataset.id || target.closest('.item').querySelector('[data-id]').dataset.id;
_this.showContextMenu(event.clientX, event.clientY, layerId);
}
});
// Hide context menu when clicking elsewhere
document.addEventListener('click', function (event) {
_this.hideContextMenu();
});
}
/**
* Show context menu for layer
*/
showContextMenu(x, y, layerId) {
var _this = this;
this.contextMenuLayerId = layerId;
// Select the layer first
if (layerId != config.layer.id) {
app.State.do_action(
new app.Actions.Select_layer_action(layerId)
);
}
var menuItems = [
{ label: 'Rename', action: 'rename' },
{ label: 'Duplicate', action: 'duplicate' },
{ label: 'Delete', action: 'delete' },
{ label: '---' },
{ label: 'Move Up', action: 'move_up' },
{ label: 'Move Down', action: 'move_down' },
{ label: '---' },
{ label: 'Scale Layer...', action: 'scale' },
{ label: 'Convert to Raster', action: 'raster' },
{ label: '---' },
{ label: 'Merge Down', action: 'merge' },
{ label: 'Flatten All', action: 'flatten' },
];
var menu = document.getElementById('layer_context_menu');
var html = '<ul class="context-menu-list">';
for (var i = 0; i < menuItems.length; i++) {
var item = menuItems[i];
if (item.label === '---') {
html += '<li class="separator"></li>';
} else {
html += '<li data-action="' + item.action + '">' + item.label + '</li>';
}
}
html += '</ul>';
menu.innerHTML = html;
menu.style.display = 'block';
menu.style.left = x + 'px';
menu.style.top = y + 'px';
// Add click handlers to menu items
menu.querySelectorAll('li[data-action]').forEach(function(item) {
item.addEventListener('click', function(e) {
e.stopPropagation();
_this.handleContextMenuAction(this.dataset.action);
_this.hideContextMenu();
});
});
}
/**
* Hide context menu
*/
hideContextMenu() {
var menu = document.getElementById('layer_context_menu');
if (menu) {
menu.style.display = 'none';
}
}
/**
* Handle context menu action
*/
handleContextMenuAction(action) {
var layerId = this.contextMenuLayerId;
switch (action) {
case 'rename':
this.Layer_rename.rename(layerId);
break;
case 'duplicate':
this.Layer_duplicate.duplicate();
break;
case 'delete':
app.State.do_action(
new app.Actions.Delete_layer_action(layerId)
);
break;
case 'move_up':
app.State.do_action(
new app.Actions.Reorder_layer_action(layerId, 1)
);
break;
case 'move_down':
app.State.do_action(
new app.Actions.Reorder_layer_action(layerId, -1)
);
break;
case 'scale':
this.Layer_scale.scale();
break;
case 'raster':
this.Layer_raster.raster();
break;
case 'merge':
this.Layer_merge.merge();
break;
case 'flatten':
this.Layer_flatten.flatten();
break;
}
}
/**
* renders layers list
*/
render_layers() {
var target_id = 'layers';
var layers = config.layers.concat().sort(
//sort function
(a, b) => b.order - a.order
);
document.getElementById(target_id).innerHTML = '';
var html = '';
if (config.layer) {
for (var i in layers) {
var value = layers[i];
var class_extra = '';
if(value.composition === 'source-atop'){
class_extra += ' shorter';
}
if (value.id == config.layer.id){
class_extra += ' active';
}
html += '<div class="item ' + class_extra + '">';
if (value.visible == true)
html += ' <button class="visibility visible trn" id="visibility" data-id="' + value.id + '" title="Hide"></button>';
else
html += ' <button class="visibility trn" id="visibility" data-id="' + value.id + '" title="Show"></button>';
html += ' <button class="delete trn" id="delete" data-id="' + value.id + '" title="Delete"></button>';
if(value.composition === 'source-atop'){
html += ' <button class="arrow_down" data-id="' + value.id + '" ></button>';
}
var layer_title = this.Helper.escapeHtml(value.name);
html += ' <button class="layer_name" id="layer_name" data-id="' + value.id + '">' + layer_title + '</button>';
html += ' <div class="clear"></div>';
html += '</div>';
//show filters
if (layers[i].filters.length > 0) {
html += '<div class="filters">';
for (var j in layers[i].filters) {
var filter = layers[i].filters[j];
var title = this.Helper.ucfirst(filter.name);
title = title.replace(/-/g, ' ');
html += '<div class="filter">';
html += ' <span class="delete" id="delete_filter" data-pid="' + layers[i].id + '" data-id="' + filter.id + '" title="delete"></span>';
html += ' <span class="layer_name" id="filter_name" data-pid="' + layers[i].id + '" data-id="' + filter.id + '" data-filter="' + filter.name + '">' + title + '</span>';
html += ' <div class="clear"></div>';
html += '</div>';
}
html += '</div>';
}
}
}
//register
document.getElementById(target_id).innerHTML = html;
if (config.LANG != 'en') {
this.Tools_translate.translate(config.LANG, document.getElementById(target_id));
}
}
}
export default GUI_layers_class;
@@ -0,0 +1,419 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../../config.js';
import menuDefinition from './../../config-menu.js';
import Tools_translate_class from './../../modules/tools/translate.js';
/**
* class responsible for rendering main menu
*/
class GUI_menu_class {
constructor() {
this.eventSubscriptions = {};
this.dropdownMaxHeightMargin = 15;
this.menuContainer = null;
this.menuBarNode = null;
this.lastFocusedMenuBarLink = 0;
this.dropdownStack = [];
this.Tools_translate = new Tools_translate_class();
}
render_main() {
this.menuContainer = document.getElementById('main_menu');
let menuTemplate = '<ul class="menu_bar" role="menubar" tabindex="0">';
for (let i = 0; i < menuDefinition.length; i++) {
const item = menuDefinition[i];
menuTemplate += this.generate_menu_bar_item_template(item, i);
}
menuTemplate += '</ul>';
this.menuContainer.innerHTML = menuTemplate;
this.menuBarNode = this.menuContainer.querySelector('[role="menubar"]');
this.menuContainer.addEventListener('click', (event) => { return this.on_click_menu(event); }, true);
this.menuContainer.addEventListener('keydown', (event) => { return this.on_key_down_menu(event); }, true);
this.menuBarNode.addEventListener('focus', (event) => { return this.on_focus_menu_bar(event); });
this.menuBarNode.addEventListener('blur', (event) => { return this.on_blur_menu_bar(event); });
this.menuBarNode.querySelectorAll('a').forEach((link) => {
link.addEventListener('focus', (event) => { return this.on_focus_menu_bar_link(event); });
});
document.body.addEventListener('mousedown', (event) => { return this.on_mouse_down_body(event); }, true);
document.body.addEventListener('touchstart', (event) => { return this.on_mouse_down_body(event); }, true);
window.addEventListener('resize', (event) => { return this.on_resize_window(event); }, true);
document.body.classList.add('loaded');
if (config.LANG != 'en') {
this.Tools_translate.translate(config.LANG, this.menuContainer);
}
}
on(eventName, callback) {
if (!this.eventSubscriptions[eventName]) {
this.eventSubscriptions[eventName] = [];
}
if (!this.eventSubscriptions[eventName].includes(callback)) {
this.eventSubscriptions[eventName].push(callback);
}
}
emit(eventName, payload, object) {
if (this.eventSubscriptions[eventName]) {
for (let callback of this.eventSubscriptions[eventName]) {
callback(payload, object);
}
}
}
generate_menu_bar_item_template(definition, index) {
return `
<li>
<a id="main_menu_0_${index}" role="menuitem" tabindex="-1" aria-haspopup="true" aria-expanded="false"
href="javascript:void(0)" data-level="0" data-index="${ index }"><span class="name trn">${ definition.name }</span></a>
</li>
`.trim();
}
generate_menu_dropdown_item_template(definition, level, index) {
if (definition.divider) {
return `
<li role="presentation">
<hr>
</li>
`.trim();
} else {
return `
<li>
<a id="main_menu_${ level }_${ index }" role="menuitem" tabindex="-1" aria-haspopup="${ (!!definition.children) + '' }"
href="${ definition.href ? definition.href : 'javascript:void(0)' }"
target="${ definition.href ? '_blank' : '_self' }"
data-level="${ level }" data-index="${ index }">
<span class="name"><span class="trn">${ definition.name }</span>${ definition.ellipsis ? ' ...' : '' }</span>
${ !!definition.shortcut ? `
<span class="shortcut"><span class="sr_only">Shortcut Key:</span> ${ definition.shortcut }</span>
` : `` }
</a>
</li>
`.trim();
}
}
on_mouse_down_body(event) {
const target = event.touches && event.touches.length > 0 ? event.touches[0].target : event.target;
// Clicked outside of menu; close dropdowns.
if (target && !this.menuContainer.contains(target)) {
this.close_child_dropdowns(0);
}
}
on_focus_menu_bar(event) {
if (document.activeElement === this.menuBarNode) {
let lastFocusedLink = this.menuBarNode.querySelector(`[data-index="${ this.lastFocusedMenuBarLink }"]`);
if (!lastFocusedLink) {
lastFocusedLink = this.menuBarNode.querySelector('a');
}
lastFocusedLink.focus();
}
}
on_focus_menu_bar_link(event) {
this.lastFocusedMenuBarLink = parseInt(event.target.getAttribute('data-index'), 10) || 0;
}
on_blur_menu_bar(event) {
// TODO
}
on_key_down_menu(event) {
const key = event.key;
const activeElement = document.activeElement;
if (activeElement && activeElement.tagName === 'A') {
const linkLevel = parseInt(activeElement.getAttribute('data-level'), 10) || 0;
const linkIndex = parseInt(activeElement.getAttribute('data-index'), 10) || 0;
const menuParent = activeElement.closest('ul');
if (linkLevel === 0) {
if (['Right', 'ArrowRight'].includes(event.key)) {
let nextLink = menuParent.querySelector(`[data-index="${ linkIndex + 1 }"]`);
if (!nextLink) {
nextLink = menuParent.querySelector(`[data-index="0"]`);
}
nextLink.focus();
}
else if (['Left', 'ArrowLeft'].includes(event.key)) {
let previousLink = menuParent.querySelector(`[data-index="${ linkIndex - 1 }"]`);
if (!previousLink) {
previousLink = menuParent.querySelector(`[data-index="${ menuParent.querySelectorAll('[data-index]').length - 1 }"]`);
}
previousLink.focus();
}
else if (['Down', 'ArrowDown'].includes(event.key)) {
if (activeElement.getAttribute('aria-haspopup') === 'true') {
event.preventDefault();
activeElement.click();
}
}
else if (event.key === 'Home') {
menuParent.querySelector(`[data-index="0"]`).focus();
}
else if (event.key === 'End') {
menuParent.querySelector(`[data-index="${ menuParent.querySelectorAll('[data-index]').length - 1 }"]`).focus();
}
else if ([' ', 'Enter'].includes(event.key)) {
event.preventDefault();
activeElement.click();
}
} else {
if (['Up', 'ArrowUp'].includes(event.key)) {
event.preventDefault();
let previousLink = menuParent.querySelector(`[data-index="${ linkIndex - 1 }"]`);
if (!previousLink) {
previousLink = menuParent.querySelector(`[data-index="${ linkIndex - 2 }"]`); // Skip dividers
}
if (!previousLink) {
previousLink = menuParent.querySelector(`[data-index="${ this.dropdownStack[linkLevel - 1].children.length - 1 }"]`);
}
previousLink.focus();
}
else if (['Down', 'ArrowDown'].includes(event.key)) {
event.preventDefault();
let nextLink = menuParent.querySelector(`[data-index="${ linkIndex + 1 }"]`);
if (!nextLink) {
nextLink = menuParent.querySelector(`[data-index="${ linkIndex + 2 }"]`); // Skip dividers
}
if (!nextLink) {
nextLink = menuParent.querySelector(`[data-index="0"]`);
}
nextLink.focus();
}
else if (['Right', 'ArrowRight'].includes(event.key)) {
if (activeElement.getAttribute('aria-haspopup') === 'true') {
activeElement.click();
}
else if (this.dropdownStack.length > 1) {
const opener = this.dropdownStack[linkLevel - 1].opener;
opener.click();
opener.focus();
}
else {
const menuBarLinkIndex = parseInt(this.dropdownStack[0].opener.getAttribute('data-index'), 10) || 0;
let nextLink = this.menuBarNode.querySelector(`[data-index="${ menuBarLinkIndex + 1 }"]`);
if (!nextLink) {
nextLink = this.menuBarNode.querySelector(`[data-index="0"]`);
}
nextLink.click();
}
}
else if (['Left', 'ArrowLeft'].includes(event.key)) {
if (this.dropdownStack.length > 1) {
const opener = this.dropdownStack[linkLevel - 1].opener;
opener.click();
opener.focus();
} else {
const menuBarLinkIndex = parseInt(this.dropdownStack[0].opener.getAttribute('data-index'), 10) || 0;
let previousLink = this.menuBarNode.querySelector(`[data-index="${ menuBarLinkIndex - 1 }"]`);
if (!previousLink) {
previousLink = this.menuBarNode.querySelector(`[data-index="${ this.menuBarNode.querySelectorAll('[data-index]').length - 1 }"]`);
}
previousLink.click();
}
}
else if (event.key === 'Home') {
menuParent.querySelector(`[data-index="0"]`).focus();
}
else if (event.key === 'End') {
menuParent.querySelector(`[data-index="${ this.dropdownStack[linkLevel - 1].children.length - 1 }"]`).focus();
}
else if ([' ', 'Enter'].includes(event.key)) {
event.preventDefault();
activeElement.click();
}
else if (['Esc', 'Escape'].includes(event.key)) {
const opener = this.dropdownStack[linkLevel - 1].opener;
opener.click();
opener.focus();
}
else if (event.key === 'Tab') {
this.close_child_dropdowns(0);
}
}
}
}
on_click_menu(event) {
const target = event.target.closest('a');
// Any link in the menu is clicked.
if (target && target.tagName === 'A') {
const hasPopup = target.getAttribute('aria-haspopup') === 'true';
if (hasPopup) {
this.toggle_dropdown(target, event.isTrusted);
} else {
this.trigger_link(target);
}
} else {
this.close_child_dropdowns(0);
}
}
on_resize_window(event) {
if (this.dropdownStack.length > 0) {
this.position_dropdowns();
}
}
toggle_dropdown(opener, isTrusted) {
const linkLevel = parseInt(opener.getAttribute('data-level'), 10) || 0;
const linkIndex = parseInt(opener.getAttribute('data-index'), 10) || 0;
if (opener.getAttribute('aria-expanded') === 'true') {
this.close_child_dropdowns(linkLevel);
} else {
const parentList = opener.closest('ul');
parentList.querySelectorAll('a').forEach((item) => {
item.setAttribute('aria-expanded', 'false');
});
opener.setAttribute('aria-expanded', true);
this.create_dropdown(opener, linkLevel, linkIndex, !isTrusted);
}
}
trigger_link(link) {
const level = parseInt(link.getAttribute('data-level'), 10) || 0;
const index = parseInt(link.getAttribute('data-index'), 10) || 0;
// Find link definition
let children = menuDefinition;
for (let i = 0; i < level; i++) {
const childIndex = this.dropdownStack[i] != null ? this.dropdownStack[i].index : index;
children = children[childIndex].children;
}
let definition = children[index];
// Close the dropdown
this.close_child_dropdowns(0);
// Emit callback events for triggered links
if (definition.target) {
this.emit('select_target', definition.target, definition);
}
else if (definition.href) {
this.emit('select_href', definition.href, null);
}
}
close_child_dropdowns(level) {
for (let i = this.dropdownStack.length - 1; i >= 0; i--) {
if (i >= level) {
this.dropdownStack[i].element.parentNode.removeChild(this.dropdownStack[i].element);
this.dropdownStack[i].opener.setAttribute('aria-expanded', false);
}
}
this.dropdownStack = this.dropdownStack.slice(0, level);
}
create_dropdown(opener, level, index, focusAfterCreation) {
this.close_child_dropdowns(level);
// Find child list in the menu definition
let children = menuDefinition;
for (let i = 0; i <= level; i++) {
const childIndex = this.dropdownStack[i] != null ? this.dropdownStack[i].index : index;
children = children[childIndex].children;
}
// Create the dropdown element, place it in DOM & position it
let dropdownElement = document.createElement('ul');
dropdownElement.className = 'menu_dropdown';
dropdownElement.role = 'menu';
dropdownElement.tabIndex = 0;
dropdownElement.setAttribute('aria-labelledby', 'main_menu_' + level + '_' + index);
let dropdownTemplate = '';
for (let i = 0; i < children.length; i++) {
dropdownTemplate += this.generate_menu_dropdown_item_template(children[i], level + 1, i);
}
dropdownElement.innerHTML = dropdownTemplate;
this.menuContainer.appendChild(dropdownElement);
if (config.LANG != 'en') {
this.Tools_translate.translate(config.LANG, this.menuContainer);
}
if (focusAfterCreation) {
dropdownElement.querySelector('a').focus();
}
this.dropdownStack.push({
children,
opener,
index,
element: dropdownElement
});
this.position_dropdowns();
}
position_dropdowns() {
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
let topNavHeight = 0;
for (let level = 0; level < this.dropdownStack.length; level++) {
const dropdownElement = this.dropdownStack[level].element;
const openerRect = this.dropdownStack[level].opener.getBoundingClientRect();
topNavHeight = openerRect.height;
const dropdownMaxHeight = vh - topNavHeight - this.dropdownMaxHeightMargin;
dropdownElement.style.maxHeight = dropdownMaxHeight + 'px';
const dropdownRect = dropdownElement.getBoundingClientRect();
if (level === 0) {
dropdownElement.style.top = (openerRect.y + openerRect.height) + 'px';
let left = openerRect.x;
if (left + dropdownRect.width > vw) {
left = openerRect.x + openerRect.width - dropdownRect.width;
}
if (left + dropdownRect.width > vw) {
left = vw - dropdownRect.width;
}
if (left < 0) {
left = 0;
}
dropdownElement.style.left = left + 'px';
} else {
let top = openerRect.y;
if (top + dropdownRect.height > vh - this.dropdownMaxHeightMargin) {
top = vh - this.dropdownMaxHeightMargin - dropdownRect.height;
}
dropdownElement.style.top = top + 'px';
let left = openerRect.x + openerRect.width + 1;
if (left + dropdownRect.width > vw) {
left = openerRect.x - dropdownRect.width - 1;
}
if (left < 0) {
if (openerRect.x + (openerRect.width / 2) > vw / 2) {
left = 1;
} else {
left = vw - dropdownRect.width - 1;
if (left < 0) {
left = 1;
}
}
}
dropdownElement.style.left = left + 'px';
}
}
}
}
export default GUI_menu_class;
@@ -0,0 +1,352 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import config from './../../config.js';
import Base_layers_class from './../base-layers.js';
var instance = null;
var template = `
<div class="canvas_preview_wrapper">
<div class="transparent-grid" id="canvas_preview_background"></div>
<canvas width="176" height="100" class="transparent" id="canvas_preview"></canvas>
</div>
<div class="canvas_preview_details">
<div class="details">
<button title="Zoom out" class="layer_add trn" id="zoom_less"">-</button>
<button title="Reset zoom level" class="layer_add trn" id="zoom_100">100%</button>
<button title="Zoom in" class="layer_add trn" id="zoom_more"">+</button>
<button title="Fit window" class="layer_add trn" id="zoom_fit">Fit</button>
</div>
<input id="zoom_range" type="range" value="100" min="50" max="1000" step="50" />
</div>
`;
/**
* GUI class responsible for rendering preview on right sidebar
*/
class GUI_preview_class {
constructor(GUI_class) {
//singleton
if (instance) {
return instance;
}
instance = this;
document.getElementById('toggle_preview').innerHTML = template;
// preview mini window size on right sidebar
this.PREVIEW_SIZE = {w: 176, h: 100};
this.canvas_offset = {x: 0, y: 0};
this.zoom_data = {
x: 0,
y: 0,
move_pos: null,
};
this.mouse_pressed = false;
this.canvas_preview = null;
if (GUI_class != undefined) {
this.GUI = GUI_class;
}
this.Base_layers = new Base_layers_class();
}
render_main_preview() {
this.canvas_preview = document.getElementById("canvas_preview")
.getContext("2d");
this.prepare_canvas();
config.need_render = true;
this.set_events();
}
set_events() {
var _this = this;
var is_touch = false;
document.addEventListener('mousedown', function (e) {
_this.mouse_pressed = true;
}, false);
document.addEventListener('mouseup', function (e) {
_this.mouse_pressed = false;
}, false);
document.addEventListener('touchstart', function (e) {
_this.mouse_pressed = true;
}, false);
document.addEventListener('touchend', function (e) {
_this.mouse_pressed = false;
}, false);
document.getElementById('zoom_range').addEventListener('input', function (e) {
_this.set_center_zoom();
_this.zoom(this.value);
}, false);
document.getElementById('zoom_range').addEventListener('change', function (e) {
//IE11
if (this.value != config.ZOOM * 100) {
_this.set_center_zoom();
_this.zoom(this.value);
}
}, false);
document.getElementById('zoom_less').addEventListener('click', function (e) {
_this.set_center_zoom();
_this.zoom(-1);
}, false);
document.getElementById('zoom_100').addEventListener('click', function (e) {
_this.zoom(100);
}, false);
document.getElementById('zoom_more').addEventListener('click', function (e) {
_this.set_center_zoom();
_this.zoom(+1);
}, false);
document.getElementById('zoom_fit').addEventListener('click', function (e) {
_this.zoom_auto();
}, false);
document.getElementById('main_wrapper').addEventListener('wheel', function (e) {
//zoom with mouse scroll
e.preventDefault();
_this.zoom_data.x = e.offsetX;
_this.zoom_data.y = e.offsetY;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail || -e.deltaY)));
if (delta > 0)
_this.zoom(+1, e);
else
_this.zoom(-1, e);
}, false);
window.addEventListener('resize', function (e) {
//resize
config.need_render = true;
}, false);
document.getElementById("canvas_preview").addEventListener('mousedown', function (e) {
if(is_touch)
return;
_this.set_zoom_position(e);
}, false);
document.getElementById("canvas_preview").addEventListener('mousemove', function (e) {
if(is_touch)
return;
if (_this.mouse_pressed == false)
return;
_this.set_zoom_position(e);
}, false);
document.getElementById("canvas_preview").addEventListener('touchstart', function (e) {
is_touch = true;
//calc canvas position offset
var bodyRect = document.body.getBoundingClientRect();
var canvas_el = document.getElementById("canvas_preview").getBoundingClientRect();
_this.canvas_offset.x = canvas_el.left - bodyRect.left;
_this.canvas_offset.y = canvas_el.top - bodyRect.top;
//change zoom offset
_this.set_zoom_position(e);
});
document.getElementById("canvas_preview").addEventListener('touchmove', function (e) {
//change zoom offset
if (_this.mouse_pressed == false)
return;
_this.set_zoom_position(e);
});
}
prepare_canvas() {
this.canvas_preview.webkitImageSmoothingEnabled = false;
this.canvas_preview.msImageSmoothingEnabled = false;
this.canvas_preview.imageSmoothingEnabled = false;
this.GUI.render_canvas_background('canvas_preview', 8);
}
render_preview_active_zone() {
if (this.canvas_preview == undefined) {
this.canvas_preview = document.getElementById("canvas_preview")
.getContext("2d");
}
//active zone
var visible_w = config.visible_width / config.ZOOM;
var visible_h = config.visible_height / config.ZOOM;
var mini_rect_w = this.PREVIEW_SIZE.w * visible_w / config.WIDTH;
var mini_rect_h = this.PREVIEW_SIZE.h * visible_h / config.HEIGHT;
var start_pos = this.Base_layers.get_world_coords(0, 0);
var mini_rect_x = start_pos.x / config.WIDTH * this.PREVIEW_SIZE.w;
var mini_rect_y = start_pos.y / config.HEIGHT * this.PREVIEW_SIZE.h;
//validate
mini_rect_x = Math.max(0, mini_rect_x);
mini_rect_y = Math.max(0, mini_rect_y);
mini_rect_w = Math.min(this.PREVIEW_SIZE.w - 1, mini_rect_w);
mini_rect_h = Math.min(this.PREVIEW_SIZE.h - 1, mini_rect_h);
if (mini_rect_x + mini_rect_w > this.PREVIEW_SIZE.w)
mini_rect_x = this.PREVIEW_SIZE.w - mini_rect_w;
if (mini_rect_y + mini_rect_h > this.PREVIEW_SIZE.h)
mini_rect_y = this.PREVIEW_SIZE.h - mini_rect_h;
if (mini_rect_x == 0 && mini_rect_y == 0 && mini_rect_w == this.PREVIEW_SIZE.w - 1
&& mini_rect_h == this.PREVIEW_SIZE.h - 1) {
//everything is visible
return;
}
//draw selected area in preview canvas
this.canvas_preview.lineWidth = 1;
this.canvas_preview.beginPath();
this.canvas_preview.rect(
Math.round(mini_rect_x) + 0.5,
Math.round(mini_rect_y) + 0.5,
mini_rect_w,
mini_rect_h
);
this.canvas_preview.fillStyle = "rgba(0, 255, 0, 0.3)";
this.canvas_preview.strokeStyle = "#00ff00";
this.canvas_preview.fill();
this.canvas_preview.stroke();
}
async zoom(recalc) {
if (recalc != undefined) {
//zoom-in or zoom-out
if (recalc == 1 || recalc == -1) {
//fix
if (config.ZOOM > 1 && config.ZOOM < 1.5) {
config.ZOOM = 1;
}
if (config.ZOOM > 0.9 && config.ZOOM < 1) {
config.ZOOM = 1;
}
//calc step
if (recalc < 0) {
//down
if (config.ZOOM > 3) {
//infinity -> 300%
config.ZOOM -= 1;
}
else if (config.ZOOM > 1) {
//300% -> 100%
config.ZOOM -= 0.5;
}
else if (config.ZOOM > 0.1) {
//100% -> 10%
config.ZOOM -= 0.1;
}
else {
//10% -> 1%
config.ZOOM -= 0.01;
}
}
else {
//up
if (config.ZOOM < 0.1) {
//1% -> 10%
config.ZOOM += 0.01;
}
else if (config.ZOOM < 1) {
//10% -> 100%
config.ZOOM += 0.1;
}
else if (config.ZOOM < 3) {
//100% -> 300%
config.ZOOM += 0.5;
}
else {
//300% -> more
config.ZOOM += 1;
}
}
}
else {
//zoom using exact value
config.ZOOM = recalc / 100;
}
config.ZOOM = Math.round(config.ZOOM * 100) / 100;
config.ZOOM = Math.max(config.ZOOM, 0.01);
config.ZOOM = Math.min(config.ZOOM, 500);
}
document.getElementById("zoom_100").innerHTML = Math.round(config.ZOOM * 100) + '%';
document.getElementById("zoom_range").value = (config.ZOOM * 100);
config.need_render = true;
this.GUI.prepare_canvas();
//sleep after last image import, it maybe not be finished yet
await new Promise(r => setTimeout(r, 10));
return true;
}
zoom_auto(only_increase) {
var container = document.getElementById('main_wrapper');
var page_w = container.clientWidth;
var page_h = container.clientHeight;
var best_width = page_w / config.WIDTH;
var best_height = page_h / config.HEIGHT;
var best_zoom = null;
best_zoom = Math.min(best_width, best_height);
if (only_increase != undefined && best_zoom > 1) {
return false;
}
this.zoom(Math.min(best_width, best_height) * 100);
}
set_center_zoom() {
this.zoom_data.x = config.visible_width / 2;
this.zoom_data.y = config.visible_height / 2;
}
set_zoom_position(event) {
var mouse_x = event.offsetX;
var mouse_y = event.offsetY;
if (event.changedTouches) {
//touch events
event = event.changedTouches[0];
mouse_x = event.pageX - this.canvas_offset.x;
mouse_y = event.pageY - this.canvas_offset.y;
}
var visible_w = config.visible_width / config.ZOOM;
var visible_h = config.visible_height / config.ZOOM;
var mini_w = this.PREVIEW_SIZE.w * visible_w / config.WIDTH;
var mini_h = this.PREVIEW_SIZE.h * visible_h / config.HEIGHT;
var change_x = (mouse_x - mini_w / 2) / this.PREVIEW_SIZE.w * config.WIDTH;
var change_y = (mouse_y - mini_h / 2) / this.PREVIEW_SIZE.h * config.HEIGHT;
var zoom_data = this.zoom_data;
zoom_data.move_pos = {};
zoom_data.move_pos.x = change_x;
zoom_data.move_pos.y = change_y;
config.need_render = true;
}
/**
* moves visible area to new position.
*
* @param {int} x global offset
* @param {int} y global offset
*/
zoom_to_position(x, y) {
var zoom_data = this.zoom_data;
zoom_data.move_pos = {};
zoom_data.move_pos.x = parseInt(x);
zoom_data.move_pos.y = parseInt(y);
config.need_render = true;
}
}
export default GUI_preview_class;
@@ -0,0 +1,390 @@
/*
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
import app from './../../app.js';
import config from './../../config.js';
import Helper_class from './../../libs/helpers.js';
import Tools_translate_class from './../../modules/tools/translate.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
import Base_gui_class from '../base-gui.js';
var instance = null;
/**
* GUI class responsible for rendering left sidebar tools
*/
class GUI_tools_class {
constructor(GUI_class) {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Helper = new Helper_class();
this.Tools_translate = new Tools_translate_class();
this.Base_gui = new Base_gui_class();
//active tool
this.active_tool = 'brush';
this.tools_modules = {};
}
load_plugins() {
var _this = this;
var ctx = document.getElementById('canvas_minipaint').getContext("2d");
var plugins_context = require.context("./../../tools/", true, /\.js$/);
plugins_context.keys().forEach(function (key) {
if (key.indexOf('Base' + '/') < 0) {
var moduleKey = key.replace('./', '').replace('.js', '');
var full_key = moduleKey;
if (moduleKey.indexOf('/') > -1) {
var parts = moduleKey.split("/");
moduleKey = parts[parts.length - 1];
}
try {
var classObj = plugins_context(key);
if (!classObj.default) return; // skip helper files without a default export
var object = new classObj.default(ctx);
var title = _this.Helper.ucfirst(object.name);
title = title.replace(/_/, ' ');
_this.tools_modules[moduleKey] = {
key: moduleKey,
full_key: full_key,
name: object.name,
title: title,
object: object,
};
//init events once
if(typeof object.load != "undefined") {
object.load();
}
} catch(e) {
console.error('[load_plugins] Failed to load ' + key + ':', e);
}
}
});
}
render_main_tools() {
this.load_plugins();
this.render_tools();
}
render_tools() {
var target_id = "tools_container";
var _this = this;
var saved_tool = this.Helper.getCookie('active_tool');
if(saved_tool == 'media' || saved_tool == 'shape') {
//bringing this back by default gives bad UX
saved_tool = null
}
if (saved_tool != null) {
this.active_tool = saved_tool;
}
//left menu
for (var i in config.TOOLS) {
var item = config.TOOLS[i];
if(item.title)
var title = item.title;
else
var title = this.Helper.ucfirst(item.name).replace(/_/, ' ');
var itemDom = document.createElement('span');
itemDom.id = item.name;
itemDom.title = title;
if (item.name == this.active_tool) {
itemDom.className = 'item trn active ' + item.name;
}
else {
itemDom.className = 'item trn ' + item.name;
}
if(item.visible === false){
itemDom.style.display = 'none';
}
//event
itemDom.addEventListener('click', function (event) {
_this.activate_tool(this.id);
});
//register
document.getElementById(target_id).appendChild(itemDom);
}
this.show_action_attributes();
new app.Actions.Activate_tool_action(this.active_tool, true).do();
this.Base_gui.check_canvas_offset();
}
async activate_tool(key) {
return app.State.do_action(
new app.Actions.Activate_tool_action(key)
);
}
action_data() {
for (var i in config.TOOLS) {
if (config.TOOLS[i].name == this.active_tool)
return config.TOOLS[i];
}
//something wrong - select first tool
this.active_tool = config.TOOLS[0].name;
return config.TOOLS[0];
}
/**
* used strings:
* "Fill", "Square", "Circle", "Radial", "Anti aliasing", "Circle", "Strict", "Burn"
*/
show_action_attributes() {
var _this = this;
var target_id = "action_attributes";
const itemContainer = document.getElementById(target_id);
itemContainer.innerHTML = "";
const attributes = this.action_data().attributes;
let itemDom;
let currentButtonGroup = null;
for (var k in attributes) {
var item = attributes[k];
var title = k[0].toUpperCase() + k.slice(1);
title = title.replace("_", " ");
if (typeof item == 'object' && typeof item.value == 'boolean' && item.icon) {
if (currentButtonGroup == null) {
currentButtonGroup = document.createElement('div');
currentButtonGroup.className = 'ui_button_group no_wrap';
itemDom = document.createElement('div');
itemDom.className = 'item ' + k;
itemContainer.appendChild(itemDom);
itemDom.appendChild(currentButtonGroup);
} else {
itemDom.classList.add(k);
}
} else {
itemDom = document.createElement('div');
itemDom.className = 'item ' + k;
itemContainer.appendChild(itemDom);
currentButtonGroup = null;
}
if (typeof item == 'boolean' || (typeof item == 'object' && typeof item.value == 'boolean')) {
//boolean - true, false
let value = item;
let icon = null;
if (typeof item == 'object') {
value = item.value;
if (item.icon) {
icon = item.icon;
}
}
const element = document.createElement('button');
element.className = 'trn';
element.type = 'button';
element.id = k;
element.innerHTML = title;
element.setAttribute('aria-pressed', value);
if (icon) {
element.classList.add('ui_icon_button');
element.classList.add('input_height');
element.innerHTML = icon;
element.title = k;
element.innerHTML = '<img style="width:16px;height:16px;" alt="'+title+'" src="images/icons/'+icon+'" />';
} else {
element.classList.add('ui_toggle_button');
}
//event
element.addEventListener('click', (event) => {
//toggle boolean
var new_value = element.getAttribute('aria-pressed') !== 'true';
const actionData = this.action_data();
const attributes = actionData.attributes;
const id = event.target.closest('button').id;
if (typeof attributes[id] === 'object') {
attributes[id].value = new_value;
} else {
attributes[id] = new_value;
}
element.setAttribute('aria-pressed', new_value);
if (actionData.on_update != undefined) {
//send event
var moduleKey = actionData.name;
var functionName = actionData.on_update;
this.tools_modules[moduleKey].object[functionName]({ key: id, value: new_value });
}
});
if (currentButtonGroup) {
currentButtonGroup.appendChild(element);
} else {
itemDom.appendChild(element);
}
}
else if (typeof item == 'number' || (typeof item == 'object' && typeof item.value == 'number')) {
//numbers
let min = 1;
let max = k === 'power' ? 100 : 999;
let value = item;
let step = null;
if (typeof item == 'object') {
value = item.value;
if (item.min != null) {
min = item.min;
}
if (item.max != null) {
max = item.max;
}
if (item.step != null) {
step = item.step;
}
}
var elementTitle = document.createElement('label');
elementTitle.innerHTML = title + ':';
elementTitle.id = 'attribute_label_' + k;
elementTitle.className = 'trn';
const elementInput = document.createElement('input');
elementInput.type = 'number';
elementInput.setAttribute('aria-labelledby', 'attribute_label_' + k);
const $numberInput = $(elementInput)
.uiNumberInput({
id: k,
min,
max,
value,
step: step || 1,
exponentialStepButtons: !step
})
.on('input', () => {
let value = $numberInput.uiNumberInput('get_value');
const id = $numberInput.uiNumberInput('get_id');
const actionData = this.action_data();
const attributes = actionData.attributes;
if (typeof attributes[id] === 'object') {
attributes[id].value = value;
} else {
attributes[id] = value;
}
if (actionData.on_update != undefined) {
//send event
var moduleKey = actionData.name;
var functionName = actionData.on_update;
this.tools_modules[moduleKey].object[functionName]({ key: id, value: value });
}
});
itemDom.appendChild(elementTitle);
itemDom.appendChild($numberInput[0]);
}
else if (typeof item == 'object') {
//select
var elementTitle = document.createElement('label');
elementTitle.innerHTML = title + ':';
elementTitle.for = k;
elementTitle.className = 'trn';
var selectList = document.createElement("select");
selectList.id = k;
const values = typeof item.values === 'function' ? item.values() : item.values;
for (let j in values) {
var option = document.createElement("option");
if (item.value == values[j]) {
option.selected = 'selected';
}
option.className = 'trn';
option.name = values[j];
option.value = values[j];
option.text = values[j];
selectList.appendChild(option);
}
//event
selectList.addEventListener('change', (event) => {
const actionData = this.action_data();
actionData.attributes[event.target.id].value = event.target.value;
if (actionData.on_update != undefined) {
//send event
var moduleKey = actionData.name;
var functionName = actionData.on_update;
const result = this.tools_modules[moduleKey].object[functionName]({ key: event.target.id, value: event.target.value });
if (result) {
// Allow the on_update function to modify the attribute value if necessary.
if (result.new_values) {
for (let key in result.new_values) {
actionData.attributes[key].value = result.new_values[key];
}
}
}
}
this.show_action_attributes();
});
itemDom.appendChild(elementTitle);
itemDom.appendChild(selectList);
}
else if (typeof item == 'string' && item[0] == '#') {
//color
var elementTitle = document.createElement('label');
elementTitle.innerHTML = title + ':';
elementTitle.for = k;
elementTitle.className = 'trn';
var colorInput = document.createElement('input');
colorInput.type = 'color';
const $colorInput = $(colorInput)
.uiColorInput({
id: k,
value: item
})
.on('change', () => {
let value = $colorInput.uiColorInput('get_value');
const id = $colorInput.uiColorInput('get_id');
const actionData = this.action_data();
actionData.attributes[id] = value;
if (actionData.on_update != undefined) {
//send event
var moduleKey = actionData.name;
var functionName = actionData.on_update;
this.tools_modules[moduleKey].object[functionName]({ key: id, value: value });
}
});
itemDom.appendChild(elementTitle);
itemDom.appendChild($colorInput[0]);
}
else {
alertify.error('Error: unsupported attribute type:' + typeof item + ', ' + k);
}
}
if (config.LANG != 'en') {
//retranslate
this.Tools_translate.translate(config.LANG);
}
}
}
export default GUI_tools_class;
@@ -0,0 +1,238 @@
/**
* Pantone color database — ~350 representative PMS colors with hex approximations.
* Source: open-source Pantone approximations (not official Pantone data).
* Format: [name, hex]
*
* Delta E matching uses LAB color space — see color_utils.js.
*/
const PANTONE_COLORS = [
// Reds & Pinks
['Pantone 485 C', '#da291c'],
['Pantone 186 C', '#c8102e'],
['Pantone 1795 C', '#ce2939'],
['Pantone 1805 C', '#ab2328'],
['Pantone 1815 C', '#833033'],
['Pantone 200 C', '#ba0c2f'],
['Pantone 201 C', '#9d2235'],
['Pantone 202 C', '#862633'],
['Pantone 206 C', '#ce0058'],
['Pantone 207 C', '#a50034'],
['Pantone 208 C', '#84254a'],
['Pantone 213 C', '#e4488a'],
['Pantone 214 C', '#d4357b'],
['Pantone 215 C', '#bb2261'],
['Pantone 219 C', '#e10069'],
['Pantone 225 C', '#d5006c'],
['Pantone 226 C', '#cb0070'],
['Pantone 485 C', '#da291c'],
['Pantone Pink C', '#e87ca0'],
['Pantone Rubine Red C', '#ce0058'],
['Pantone Rhodamine Red C', '#e10096'],
// Oranges
['Pantone 021 C', '#fe5000'],
['Pantone 151 C', '#ff7900'],
['Pantone 152 C', '#e87722'],
['Pantone 153 C', '#cb6015'],
['Pantone 158 C', '#e8642c'],
['Pantone 165 C', '#fc4c02'],
['Pantone 166 C', '#e55302'],
['Pantone 167 C', '#be4b00'],
['Pantone 1495 C', '#ff8200'],
['Pantone 1505 C', '#ff671f'],
['Pantone Orange 021 C', '#fe5000'],
['Pantone Warm Red C', '#f9423a'],
// Yellows
['Pantone Yellow C', '#fedd00'],
['Pantone 101 C', '#f9e84e'],
['Pantone 102 C', '#fce300'],
['Pantone 103 C', '#c5a900'],
['Pantone 104 C', '#af9800'],
['Pantone 108 C', '#f6d500'],
['Pantone 109 C', '#ffd100'],
['Pantone 110 C', '#d4af00'],
['Pantone 115 C', '#fbdb65'],
['Pantone 116 C', '#ffcd00'],
['Pantone 117 C', '#c79200'],
['Pantone 123 C', '#ffc72c'],
['Pantone 124 C', '#e6a817'],
['Pantone 130 C', '#f0aa00'],
['Pantone 1205 C', '#f5e1a4'],
['Pantone 1215 C', '#f5cf7e'],
['Pantone 1225 C', '#fbb040'],
['Pantone 1235 C', '#f7941d'],
['Pantone 1245 C', '#d4890a'],
['Pantone Gold C', '#af8c00'],
// Greens
['Pantone Green C', '#00ab84'],
['Pantone 354 C', '#00b140'],
['Pantone 355 C', '#009a44'],
['Pantone 356 C', '#007a3d'],
['Pantone 361 C', '#43b02a'],
['Pantone 362 C', '#3d9a31'],
['Pantone 363 C', '#347d2c'],
['Pantone 368 C', '#78be20'],
['Pantone 369 C', '#5da31c'],
['Pantone 370 C', '#4a7729'],
['Pantone 375 C', '#97d700'],
['Pantone 376 C', '#72b200'],
['Pantone 382 C', '#c4d600'],
['Pantone 390 C', '#a8ad00'],
['Pantone 334 C', '#00855d'],
['Pantone 335 C', '#006a52'],
['Pantone 336 C', '#00573f'],
['Pantone 340 C', '#00843d'],
['Pantone 341 C', '#00693c'],
['Pantone 342 C', '#215732'],
['Pantone 347 C', '#009a44'],
['Pantone 348 C', '#007a3d'],
['Pantone 349 C', '#215732'],
['Pantone 3415 C', '#00665c'],
['Pantone 3425 C', '#006a52'],
// Teals & Cyans
['Pantone Process Cyan C', '#0085ca'],
['Pantone 306 C', '#00b5e2'],
['Pantone 307 C', '#007dba'],
['Pantone 308 C', '#005f86'],
['Pantone 313 C', '#00b0ca'],
['Pantone 314 C', '#0093ab'],
['Pantone 315 C', '#007395'],
['Pantone 320 C', '#009ca6'],
['Pantone 321 C', '#008c95'],
['Pantone 322 C', '#007680'],
['Pantone 326 C', '#00b2a9'],
['Pantone 327 C', '#007a74'],
['Pantone 328 C', '#006E61'],
['Pantone 3262 C', '#00b2a9'],
['Pantone 3272 C', '#00a3ad'],
['Pantone 3282 C', '#008c95'],
['Pantone 3292 C', '#005f6a'],
// Blues
['Pantone Reflex Blue C', '#001489'],
['Pantone Blue 072 C', '#10069f'],
['Pantone 279 C', '#418fde'],
['Pantone 280 C', '#003087'],
['Pantone 281 C', '#002d72'],
['Pantone 286 C', '#0033a0'],
['Pantone 287 C', '#003087'],
['Pantone 288 C', '#002d72'],
['Pantone 293 C', '#0032a0'],
['Pantone 294 C', '#002b6c'],
['Pantone 295 C', '#002244'],
['Pantone 300 C', '#0057a8'],
['Pantone 301 C', '#005596'],
['Pantone 302 C', '#003f72'],
['Pantone 2728 C', '#2251b8'],
['Pantone 2738 C', '#1b1464'],
['Pantone 2748 C', '#0f1f8a'],
['Pantone 2758 C', '#13234b'],
['Pantone Bright Blue C', '#0087c8'],
['Pantone 298 C', '#5bc8f5'],
['Pantone 297 C', '#7bc4e2'],
// Purples & Violets
['Pantone Violet C', '#440099'],
['Pantone 2587 C', '#8246af'],
['Pantone 2597 C', '#6b1f7c'],
['Pantone 2607 C', '#5e2175'],
['Pantone 2617 C', '#522d6d'],
['Pantone 2627 C', '#401752'],
['Pantone 2665 C', '#9678d3'],
['Pantone 2685 C', '#43009a'],
['Pantone 2695 C', '#312068'],
['Pantone 2705 C', '#8085c9'],
['Pantone 2715 C', '#6e6bbf'],
['Pantone 2725 C', '#4f52af'],
['Pantone 2735 C', '#1f1a6e'],
['Pantone 2745 C', '#1b1747'],
['Pantone Ultra Violet C', '#5f4b8b'],
['Pantone 259 C', '#6c2e8e'],
['Pantone 266 C', '#6a2bb8'],
['Pantone 267 C', '#521b8a'],
['Pantone 268 C', '#43205e'],
['Pantone 269 C', '#31184e'],
['Pantone 253 C', '#b968c7'],
['Pantone 254 C', '#aa4da0'],
['Pantone 2562 C', '#c294d6'],
// Magentas
['Pantone Process Magenta C', '#d50087'],
['Pantone Magenta 0521 C', '#d6006f'],
['Pantone 233 C', '#c5007f'],
['Pantone 234 C', '#a50064'],
['Pantone 235 C', '#8c0056'],
['Pantone 239 C', '#db5aa4'],
['Pantone 240 C', '#bf4e99'],
// Browns & Tans
['Pantone 469 C', '#6b3d2e'],
['Pantone 470 C', '#8c4a2f'],
['Pantone 471 C', '#a05b38'],
['Pantone 476 C', '#4e3629'],
['Pantone 477 C', '#5c3d2e'],
['Pantone 478 C', '#6d4535'],
['Pantone 483 C', '#7a2e22'],
['Pantone 484 C', '#9b3423'],
['Pantone 4625 C', '#4a1c0e'],
['Pantone 4635 C', '#7d3c1a'],
['Pantone 4645 C', '#a45f3a'],
['Pantone 4655 C', '#b87246'],
['Pantone 463 C', '#7d5326'],
['Pantone 464 C', '#8b5e27'],
['Pantone 465 C', '#9e7232'],
['Pantone 4505 C', '#8a7252'],
['Pantone 4515 C', '#9e8866'],
['Pantone 4525 C', '#b39e7a'],
['Pantone Tan C', '#d2b48c'],
// Grays
['Pantone Cool Gray 1 C', '#d9d9d6'],
['Pantone Cool Gray 2 C', '#d0d0ce'],
['Pantone Cool Gray 3 C', '#c8c9c7'],
['Pantone Cool Gray 4 C', '#bbbcbc'],
['Pantone Cool Gray 5 C', '#b1b3b3'],
['Pantone Cool Gray 6 C', '#a7a8aa'],
['Pantone Cool Gray 7 C', '#97999b'],
['Pantone Cool Gray 8 C', '#888b8d'],
['Pantone Cool Gray 9 C', '#75787b'],
['Pantone Cool Gray 10 C','#63666a'],
['Pantone Cool Gray 11 C','#53565a'],
['Pantone Warm Gray 1 C', '#d8d3cb'],
['Pantone Warm Gray 2 C', '#cec6ba'],
['Pantone Warm Gray 3 C', '#c4bbad'],
['Pantone Warm Gray 4 C', '#bbb0a2'],
['Pantone Warm Gray 5 C', '#b0a596'],
['Pantone Warm Gray 6 C', '#a39891'],
['Pantone Warm Gray 7 C', '#968c85'],
['Pantone Warm Gray 8 C', '#8a7f76'],
['Pantone Warm Gray 9 C', '#7d7368'],
['Pantone Warm Gray 10 C','#72685d'],
['Pantone Warm Gray 11 C','#655f56'],
['Pantone 420 C', '#c7c7c4'],
['Pantone 421 C', '#b5b6b3'],
['Pantone 422 C', '#a4a4a1'],
['Pantone 423 C', '#929291'],
['Pantone 424 C', '#7f7f7d'],
['Pantone 425 C', '#6c6c6c'],
['Pantone 426 C', '#404040'],
// Black & White
['Pantone Black C', '#2b2926'],
['Pantone Black 6 C','#101820'],
['Pantone White', '#f2f0eb'],
// Special / Brand colors
['Pantone 021 C', '#fe5000'], // Harley-Davidson orange area
['Pantone 484 C', '#9b3423'],
['Pantone Bright Red C', '#f22613'],
['Pantone 3005 C', '#0076c2'],
['Pantone 3015 C', '#006298'],
['Pantone 3025 C', '#005274'],
['Pantone 3035 C', '#00445d'],
];
export default PANTONE_COLORS;
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "حدثت مشكلة أثناء إزالة محفوظات التراجع. هو - هي",
"About": "حول",
"Active": "نشيط",
"Aden": "عدن",
"Advanced": "متقدم",
"All": "الجميع",
"Alpha": "ألفا",
"Alpha:": "ألفا:",
"Anonymous": "مجهول",
"Anti aliasing": "مكافحة التعرج",
"Application markup may have changed,": "ربما تم تغيير ترميز التطبيق،",
"Arial": "اريال",
"Arrow": "سهم",
"ArrowDown": "السهم للاسفل",
"ArrowLeft": "السهم لليسار",
"ArrowRight": "السهم الأيمن",
"ArrowUp": "ارووب",
"Author:": "مؤلف:",
"Auto Adjust Colors": "ضبط تلقائي للألوان",
"Auto Kerning": "تقنين تلقائي لتقنين الأحرف",
"Average:": "متوسط:",
"Backspace": "مسافة للخلف",
"Base": "يتمركز",
"Basic": "أساسي",
"Black and White": "اسود و ابيض",
"Blue": "أزرق",
"Blue channel:": "القناة الزرقاء:",
"Blueprint": "مخطط",
"Blur Radius:": "نصف قطر التمويه:",
"Blur Tool": "أداة طمس",
"Blur power:": "قوة طمس:",
"Borders": "الحدود",
"Bottom": "قاع",
"Bottom to Top": "من الأسفل للأعلى",
"Bounds:": "الحدود:",
"Box": "علبة",
"Box Blur": "مربع طمس",
"Box blur": "مربع طمس",
"Brightness": "سطوع",
"Brightness:": "سطوع:",
"Bulge\/Pinch Tool": "أداة انتفاخ \/ قرصة",
"Burn": "حرق",
"Can not animate 1 layer.": "لا يمكن تحريك طبقة واحدة.",
"Can not find previous layer.": "لا يمكن العثور على الطبقة السابقة.",
"Can not use this tool on current layer: image already takes all area.": "لا يمكن استخدام هذه الأداة على الطبقة الحالية: الصورة تشغل المساحة بأكملها بالفعل.",
"Cancel": "يلغي",
"Canvas Size": "حجم قماش",
"Center": "مركز",
"Center x:": "المركز x:",
"Center y:": "مركز ص:",
"Center:": "مركز:",
"Change Composition": "تغيير التكوين",
"Change Layer Details": "تغيير تفاصيل الطبقة",
"Change Opacity": "تغيير التعتيم",
"Channel:": "قناة:",
"Circle": "دائرة",
"Clarendon": "كلاريندون",
"Clear": "واضح",
"Clear Selection": "التحديد الواضح",
"Clone Tool": "أداة استنساخ",
"Clone count:": "عدد النسخ:",
"Clone tool disabled for resized image. Please rasterize first.": "تم تعطيل أداة النسخ للصورة التي تم تغيير حجمها. يرجى التنقيط أولا.",
"Cloned edges": "حواف مستنسخة",
"Close": "يغلق",
"Color #": "اللون #",
"Color Corrections": "تصحيحات اللون",
"Color Palette": "لوحة الألوان",
"Color Zoom": "تكبير اللون",
"Color alpha value can not be zero.": "لا يمكن أن تكون قيمة ألفا للون صفراً.",
"Color to Alpha": "لون ألفا",
"Color zoom": "تكبير اللون",
"Color:": "اللون:",
"Colors": "الألوان",
"Colors:": "الألوان:",
"Common Filters": "مرشحات مشتركة",
"Composition": "تكوين",
"Composition:": "تكوين:",
"Content Fill": "تعبئة المحتوى",
"Contrast": "مقابلة",
"Contrast:": "مقابلة:",
"Convert layer to raster": "تحويل الطبقة إلى النقطية",
"Convert to Raster": "تحويل إلى نقطي",
"Copy Selection": "نسخ التحديد",
"Copy to Clipboard": "نسخ إلى الحافظة",
"Courier": "ساعي",
"Crop Tool": "أداة المحاصيل",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "القص على الطبقة التي تم تدويرها غير مدعوم. قم بتحويله إلى خطوط المسح للمتابعة.",
"Ctrl + C": "السيطرة + ج",
"Ctrl+A": "السيطرة + أ",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "السيطرة+P",
"Ctrl+V": "السيطرة + V.",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "السيطرة + Z",
"Current": "تيار",
"Current Color Preview": "معاينة اللون الحالي",
"Custom": "مخصص",
"Data URL": "URL البيانات",
"Data URL:": "URL البيانات:",
"Decrease": "تخفيض",
"Decrease Color Depth": "تقليل عمق اللون",
"Degree:": "درجة:",
"Del": "ديل",
"Delete": "حذف",
"Delete Selection": "حذف التحديد",
"Denoise": "يقلل الضوضاء",
"Desaturate Tool": "أداة إزالة التشبع",
"Description:": "وصف:",
"Deutsch": "الألمانية",
"Differences": "اختلافات",
"Differences Down": "الخلافات أسفل",
"Direction:": "اتجاه:",
"Dither": "ثبات",
"Dithering:": "التردد:",
"Dominant color:": "اللون السائد:",
"Dot Screen": "شاشة نقطية",
"Down": "أسفل",
"Duplicate": "ينسخ",
"Duplicate Layer": "طبقة مكررة",
"Duplicate layer": "طبقة مكررة",
"Dynamic": "متحرك",
"Edge": "حافة",
"Edit": "يحرر",
"Edit text...": "تحرير النص...",
"Effect browser": "متصفح التأثير",
"Effects": "تأثيرات",
"Effects browser": "متصفح التأثيرات",
"Email:": "بريد الالكتروني:",
"Emboss": "زخرف",
"Empty selection": "اختيار فارغ",
"Empty selection or type not image.": "اختيار فارغ أو اكتب ليس صورة.",
"Enable autoresize:": "تمكين إعادة الحجم التلقائي:",
"End": "نهاية",
"English": "الإنجليزية",
"English (UK)": "الإنجليزية (المملكة المتحدة)",
"Enrich": "يثرى",
"Enter": "يدخل",
"Erase Tool": "أداة المحو",
"Erase on rotate object is disabled. Please rasterize first.": "تم تعطيل المسح عند تدوير الكائن. يرجى التنقيط أولا.",
"Error": "خطأ",
"Error connecting to service.": "خطأ في الاتصال بالخدمة.",
"Error loading the list of fonts from Google.": "حدث خطأ أثناء تحميل قائمة الخطوط من Google.",
"Error registering service worker": "خطأ في تسجيل عامل الخدمة",
"Error: can not find filter:": "خطأ: لا يمكن العثور على عامل التصفية:",
"Error: can not find layer with id:": "خطأ: لا يمكن العثور على طبقة بالمعرف:",
"Error: missing details event target": "خطأ: تفاصيل الهدف حدث مفقود",
"Error: unknown layer type:": "خطأ: نوع طبقة غير معروف:",
"Error: unsupported attribute type:": "خطأ: نوع السمة غير مدعوم:",
"Esc": "خروج",
"Escape": "يهرب",
"Español": "الاسبانية",
"Expand edges": "قم بتوسيع الحواف",
"Exponent:": "الأس:",
"Export": "يصدر",
"External": "خارجي",
"Factor:": "عامل:",
"File": "ملف",
"File name:": "اسم الملف:",
"File size:": "حجم الملف:",
"Fill": "ملء",
"Fill Tool": "أداة التعبئة",
"Fit": "ملائم",
"Fit Window": "تناسب النافذة",
"Fit window": "نافذة مناسبة",
"Flatten Image": "تسطيح الصورة",
"Flip": "يواجه",
"FloydSteinberg-serpentine": "FloydSteinberg-serpentine",
"Font": "الخط",
"Français": "الفرنسية",
"Full HD, 1080p": "دقة Full HD ، 1080 بكسل",
"Full Screen": "تكبير الشاشة",
"Full layers data": "بيانات الطبقات الكاملة",
"Gap:": "الفارق:",
"Gaussian Blur": "التمويه الضبابي",
"Gif delay:": "تأخير Gif:",
"Gingham": "القماش القطني",
"GitHub:": "جيثب:",
"Gradient Radius:": "نصف قطر التدرج:",
"Grains": "بقوليات",
"Graphics Interchange Format": "تنسيق تبادل الرسومات",
"Gray": "رمادي",
"Grayscale": "تدرج الرمادي",
"Greek": "اليونانية",
"Green": "لون أخضر",
"Green channel:": "القناة الخضراء:",
"Greyscale:": "الرمادي:",
"Grid": "شبكة",
"Grid on\/off": "الشبكة على \/ قبالة",
"Guides": "خطوط إرشاد",
"Guides enabled.": "تم تمكين الأدلة.",
"H Radius:": "نصف قطر H:",
"H. Align:": "ح. محاذاة:",
"Heatmap": "خريطة الحرارة",
"Height (%):": "ارتفاع (٪):",
"Height:": "ارتفاع:",
"Help": "مساعدة",
"Helvetica": "هيلفيتيكا",
"Hermite": "هيرمايت",
"Hex": "عرافة",
"Hide": "يخفي",
"Histogram": "الرسم البياني",
"Histogram:": "الرسم البياني:",
"Home": "الصفحة الرئيسية",
"Horizontal": "أفقي",
"Horizontal Alignment": "المحاذاة الأفقية",
"Horizontal blur:": "طمس أفقي:",
"Horizontal:": "أفقي:",
"Hue": "مسحة",
"Hue Rotate": "تدوير هوى",
"Hue:": "مسحة:",
"Image": "صورة",
"Image data with multi-layers. Can be opened using miniPaint -": "بيانات الصورة متعددة الطبقات. يمكن فتحه باستخدام miniPaint -",
"Impact": "تأثير",
"In proportion:": "في نسبة:",
"Increase": "زيادة",
"Information": "معلومة",
"Inkwell": "محبرة",
"Insert": "إدراج",
"Insert guides": "أدلة إدراج",
"Insert new layer": "أدخل طبقة جديدة",
"Instagram Filters": "مرشحات Instagram",
"Invalid Hex Code": "رمز سداسي عشري غير صالح",
"Italiano": "ايطالي",
"JPG\/JPEG Format": "تنسيق JPG \/ JPEG",
"Kerning:": "تقنين الأحرف:",
"Key-Points": "النقاط الرئيسية",
"KeyU": "KeyU",
"Keyboard Shortcuts": "اختصارات لوحة المفاتيح",
"Keyword:": "الكلمة الرئيسية:",
"Lanczos": "لانكوز",
"Landscape": "منظر جمالي",
"Language": "لغة",
"Last modified": "آخر تعديل",
"Layer": "طبقة",
"Layer details": "تفاصيل الطبقة",
"Layer is empty.": "الطبقة فارغة.",
"Layer is not compatible with resize": "الطبقة غير متوافقة مع تغيير الحجم",
"Layer is vector, convert it to raster to apply this tool.": "الطبقة متجهية ، قم بتحويلها إلى خطوط نقطية لتطبيق هذه الأداة.",
"Layers": "طبقات",
"Layers:": "طبقات:",
"Layout:": "تَخطِيط:",
"Left": "اليسار",
"Left to Right": "من اليسار إلى اليمين",
"Level:": "مستوى:",
"Levels:": "المستويات:",
"Lietuvių": "ليتوفيتش",
"Lo-fi": "Lo-fi",
"Luminance:": "الانارة:",
"Luminosity": "لمعان",
"Magic Eraser Tool": "أداة ماجيك ممحاة",
"Merge Down": "دمج أسفل",
"Merge Layers": "دمج الطبقات",
"Merged": "مندمجة",
"Metrics": "المقاييس",
"Middle": "وسط",
"Missing at least 1 size parameter.": "معلمة حجم واحدة مفقودة على الأقل.",
"Missing permissions to write to Clipboard.cc": "أذونات مفقودة للكتابة إلى Clipboard.cc",
"Mode:": "الوضع:",
"Module function not found.": "لم يتم العثور على وظيفة الوحدة النمطية.",
"Modules class not found:": "فئة الوحدات غير موجودة:",
"Monospace": "مونوسبيس",
"Mosaic": "فسيفساء",
"Mouse:": "الفأر:",
"Move": "يتحرك",
"Move Layer": "تحريك الطبقة",
"Move layer down": "انقل الطبقة إلى الأسفل",
"Move layer up": "حرك الطبقة لأعلى",
"Name:": "اسم:",
"Negative": "سلبي",
"New": "جديد",
"New Bezier Layer": "طبقة بيزيير جديدة",
"New Brush Layer": "طبقة فرشاة جديدة",
"New Ellipse Layer": "طبقة Ellipse جديدة",
"New File": "ملف جديد",
"New Gradient Layer": "طبقة متدرجة جديدة",
"New Layer": "طبقة جديدة",
"New Line Layer": "طبقة خط جديدة",
"New Pencil Layer": "طبقة قلم رصاص جديدة",
"New Polygon Layer": "طبقة مضلعة جديدة",
"New Rectangle Layer": "طبقة مستطيل جديدة",
"New Text Layer": "طبقة نص جديدة",
"New file": "ملف جديد",
"New from Selection": "جديد من التحديد",
"New layer": "طبقة جديدة",
"Next": "التالي",
"Night Vision": "الرؤية الليلية",
"None": "لا أحد",
"Nothing is selected.": "لم يتم اختيار شيء.",
"Offset X:": "تعويض X:",
"Offset Y:": "تعويض ص:",
"Oil": "زيت",
"Ok": "موافق",
"Online image editor.": "محرر الصور على الإنترنت.",
"Opacity": "العتامة",
"Opacity:": "العتامة:",
"Open": "فتح",
"Open Data URL": "فتح URL البيانات",
"Open Directory": "الدليل المفتوح",
"Open File": "افتح ملف",
"Open File Data URL": "فتح ملف بيانات URL",
"Open File URL": "فتح ملف URL",
"Open File Webcam": "افتح ملف كاميرا الويب",
"Open Image": "صورة مفتوحة",
"Open JSON File": "افتح ملف JSON",
"Open Test Template": "افتح نموذج الاختبار",
"Open URL": "رابط مفتوح",
"Open data URL": "فتح URL البيانات",
"Open from Webcam": "افتح من كاميرا الويب",
"Original Size": "الحجم الأصلي",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - تحويل الصورة إلى SVG",
"PageDown": "اسفل الصفحة",
"PageUp": "PageUp",
"Palette": "لوحة",
"Parameter #1:": "المعلمة # 1:",
"Parameter #2:": "المعلمة # 2:",
"Paste": "معجون",
"Pencil": "قلم",
"Percentage:": "النسبة المئوية:",
"Pixels:": "بكسل:",
"Placeholder comment for color channels": "تعليق العنصر النائب لقنوات الألوان",
"Placeholder comment for color picker": "تعليق العنصر النائب لمنتقي الألوان",
"Placeholder comment for color swatches": "تعليق العنصر النائب لحوامل اللون",
"Portable Network Graphics": "رسومات الشبكة المحمولة",
"Portrait": "لَوحَة",
"Português": "البرتغالية",
"Position:": "موقع:",
"Power:": "قوة:",
"Preview": "معاينة",
"Previous": "سابق",
"Previous layer must be image, convert it to raster to apply this tool.": "يجب أن تكون الطبقة السابقة صورة ، قم بتحويلها إلى نقطية لتطبيق هذه الأداة.",
"Print": "مطبعة",
"Quality:": "جودة:",
"Quick Load": "تحميل سريع",
"Quick Save": "حفظ سريع",
"REMOVE.BG - Remove Image Background": "؛ REMOVE.BG - إزالة خلفية الصورة",
"Radial": "شعاعي",
"Radial gradient": "شعاعي التدرج",
"Radius:": "نصف القطر:",
"Range:": "نطاق:",
"Red": "أحمر",
"Red channel:": "القناة الحمراء:",
"Redo": "إعادة",
"Remove all": "حذف الكل",
"Rename": "إعادة تسمية",
"Rename Layer": "إعادة تسمية الطبقة",
"Rendered with errors.": "قدمت مع وجود أخطاء.",
"Rendering...": "استدعاء...",
"Replace Color": "استبدل اللون",
"Replace color": "استبدل اللون",
"Replacement:": "إستبدال:",
"Report Issues": "الإبلاغ عن المشكلات",
"Reset": "إعادة ضبط",
"Resize": "تغيير الحجم",
"Resize Boundary": "تغيير حجم الحدود",
"Resize Layer": "طبقة تغيير الحجم",
"Resize Layers": "تغيير حجم الطبقات",
"Resize Text Layer": "تغيير حجم طبقة النص",
"Resized as background": "تم تغيير الحجم كخلفية",
"Resized:": "تم تغيير الحجم:",
"Resolution:": "القرار:",
"Restore Alpha": "استعادة ألفا",
"Right": "حق",
"Right angle:": "زاوية مستقيمة:",
"Right to Left": "من اليمين الى اليسار",
"Rotate": "استدارة",
"Rotate Layer": "تدوير طبقة",
"Rotate is not supported on this type of object. Convert to raster?": "التدوير غير مدعوم في هذا النوع من الكائنات. تحويل إلى نقطية؟",
"Rotate left": "استدر يسارا",
"Rotate:": "استدارة:",
"Ruler": "مسطرة",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - ضغط ومقارنة الصور",
"Saturate": "تشبع",
"Saturation": "التشبع",
"Saturation:": "التشبع:",
"Save As": "حفظ باسم",
"Save As Data URL": "حفظ باسم URL البيانات",
"Save as": "حفظ باسم",
"Save as type:": "حفظ كنوع:",
"Save layers:": "حفظ الطبقات:",
"Scaling up is not supported in Hermite, using Lanczos.": "التوسع غير مدعوم في Hermite ، باستخدام Lanczos.",
"Scroll down": "حرك الفأرة لأسفل",
"Scroll up": "انتقل إلى أعلى",
"Search": "بحث",
"Search Images": "البحث عن الصور",
"Search for Font": "البحث عن الخط",
"Search:": "يبحث:",
"Select All": "اختر الكل",
"Select Text Layer": "حدد طبقة النص",
"Select object tool": "حدد أداة الكائن",
"Selected": "المحدد",
"Selection Tool": "آلة الاختيار",
"Sensitivity:": "حساسية:",
"Separated": "منفصل",
"Separated (original types)": "منفصل (الأنواع الأصلية)",
"Sepia": "بني داكن",
"Set Image Size": "ضبط حجم الصورة",
"Settings": "إعدادات",
"Shadow": "ظل",
"Shapes": "الأشكال",
"Shapes (H)": "الأشكال (ح)",
"Sharpen": "شحذ",
"Sharpen Tool": "أداة شحذ",
"Sharpen:": "شحذ:",
"Shift + S": "التحول + س",
"Shortcut Key:": "مفتاح الاختصار:",
"Show": "يعرض",
"Show \/ Hide": "اظهر المخفي",
"Show file size:": "إظهار حجم الملف:",
"Simple": "بسيط",
"Size is too big, max": "الحجم كبير جدًا ، الحد الأقصى",
"Size:": "مقاس:",
"Skip - layer must be image.": "تخطي - يجب أن تكون الطبقة عبارة عن صورة.",
"Solarize": "شمسي",
"Sorry, cold not load getUserMedia() data:": "عذرا ، لا تقم بتحميل بيانات getUserMedia ():",
"Sorry, image could not be loaded.": "عذرا ، الصورة لا يمكن تحميلها.",
"Sorry, image could not be loaded. Try copy image and paste it.": "عذرا ، الصورة لا يمكن تحميلها. حاول نسخ الصورة ولصقها.",
"Sorry, image is too big, max 5 MB.": "عذرًا ، الصورة كبيرة جدًا ، بحد أقصى 5 ميجا بايت.",
"Source coordinates saved.": "تم حفظ إحداثيات المصدر.",
"Source is empty, right click on image or use long press to save source position.": "المصدر فارغ ، انقر بزر الماوس الأيمن على الصورة أو استخدم الضغط لفترة طويلة لحفظ موضع المصدر.",
"Sprites": "العفاريت",
"Square": "مربع",
"Stream:": "مجرى:",
"Strength:": "قوة:",
"Strict": "صارم",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - ضغط PNG و JPEG",
"Tab": "فاتورة غير مدفوعة",
"Tag Image File Format": "تنسيق ملف صورة العلامة",
"Tahoma": "تاهوما",
"Target:": "استهداف:",
"The quick brown fox jumps over the lazy dog.": "الثعلب البني السريع يقفز فوق الكلب الكسول.",
"There": "هناك",
"There are no layers behind.": "لا توجد طبقات خلف.",
"There is only 1 layer.": "هناك طبقة واحدة فقط.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "يجب أن تحتوي هذه الطبقة على صورة. يرجى تحويله إلى نقطية لتطبيق هذه الأداة.",
"Tilt Shift": "تحول الإمالة",
"Times New Roman": "تايمز نيو رومان",
"Toaster": "محمصة",
"Toggle": "تبديل",
"Toggle Color Channels": "تبديل قنوات الألوان",
"Toggle Color Picker": "تبديل منتقي الألوان",
"Toggle Menu": "تبديل القائمة",
"Toggle Swatches": "تبديل العينات",
"Tools": "أدوات",
"Top": "قمة",
"Top to Bottom": "من اعلى لاسفل",
"Total pixels:": "إجمالي وحدات البكسل:",
"Translate": "ترجمة",
"Translate Layer": "طبقة الترجمة",
"Translate error, can not find dictionary:": "خطأ في الترجمة ، لا يمكن العثور على القاموس:",
"Transparent:": "شفاف:",
"Trim": "تقليم",
"Trim Layers": "طبقات القطع",
"Trim borders:": "تقليم الحدود:",
"Trim layer:": "طبقة القطع:",
"Trim white color?": "تقليم اللون الأبيض؟",
"Type:": "اكتب:",
"Türkçe": "Türkçe",
"Undo": "الغاء التحميل",
"Unique colors:": "ألوان فريدة:",
"Up": "فوق",
"Update": "تحديث",
"Update Brush Layer": "تحديث طبقة الفرشاة",
"Update Pencil Layer": "تحديث طبقة القلم الرصاص",
"Update guides": "أدلة التحديث",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "استخدم اختصار لوحة المفاتيح Ctrl + V للصق من الحافظة.",
"V Radius:": "نصف القطر الخامس:",
"V. Align:": "V. محاذاة:",
"Valencia": "فالنسيا",
"Verdana": "فيردانا",
"Version:": "الإصدار:",
"Vertical": "رأسي",
"Vertical Alignment": "انحياز عمودي",
"Vertical blur:": "التمويه العمودي:",
"Vertical:": "رأسي:",
"Vibrance": "حيوية",
"View": "رأي",
"Vignette": "المقالة القصيرة",
"ViliusL": "ViliusL",
"Vintage": "كلاسيكي",
"Webcam": "كاميرا ويب",
"Webcam #": "كاميرا ويب #",
"Website:": "موقع الكتروني:",
"Weppy File Format": "تنسيق ملف Weppy",
"Width (%):": "عرض (٪):",
"Width:": "عرض:",
"Windows Bitmap": "Windows Bitmap",
"Word": "كلمة",
"Word + Letter": "كلمة + حرف",
"Wrap At:": "التفاف في:",
"Wrap:": "لف:",
"Wrong dimensions": "أبعاد خاطئة",
"Wrong file type, must be image or json.": "نوع الملف غير صحيح ، يجب أن يكون صورة أو json.",
"X end:": "نهاية X:",
"X position:": "المركز العاشر:",
"X start:": "بداية X:",
"X-Pro II": "اكس برو الثاني",
"Y end:": "نهاية ص:",
"Y position:": "موقف ص:",
"Y start:": "بداية Y:",
"You can also drag and drop items into browser.": "يمكنك أيضًا سحب العناصر وإفلاتها في المتصفح.",
"Your browser does not support canvas or JavaScript is not enabled.": "لا يدعم المستعرض الخاص بك اللوحة القماشية أو لم يتم تمكين JavaScript.",
"Your browser does not support this format.": "متصفحك لا يدعم هذا التنسيق.",
"Your search did not match any images.": "بحثك لم يطابق أي صور.",
"Zoom": "تكبير",
"Zoom Blur": "زووم بلور",
"Zoom In": "تكبير",
"Zoom Out": "تصغير",
"Zoom blur": "زووم طمس",
"Zoom in": "تكبير",
"Zoom out": "تصغير",
"Zoom:": "تكبير:"
}
@@ -0,0 +1,3 @@
/*
* Fr - Toad06
*/
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Beim Entfernen des Rückgängig-Verlaufs ist ein Problem aufgetreten. Es",
"About": "Über",
"Active": "Aktiv",
"Aden": "Aden",
"Advanced": "Fortgeschritten",
"All": "Alle",
"Alpha": "Alpha",
"Alpha:": "Alpha:",
"Anonymous": "Anonym",
"Anti aliasing": "Kantenglättung",
"Application markup may have changed,": "Das Anwendungs-Markup hat sich möglicherweise geändert.",
"Arial": "Arial",
"Arrow": "Pfeil",
"ArrowDown": "Pfeil nach unten",
"ArrowLeft": "Pfeil links",
"ArrowRight": "Pfeil rechts",
"ArrowUp": "Pfeil nach oben",
"Author:": "Autor:",
"Auto Adjust Colors": "Automatische Farbeinstellung",
"Auto Kerning": "Auto Kerning",
"Average:": "Durchschnitt:",
"Backspace": "Rücktaste",
"Base": "Basis",
"Basic": "Basic",
"Black and White": "Schwarz und weiß",
"Blue": "Blau",
"Blue channel:": "Blauer Kanal:",
"Blueprint": "Entwurf",
"Blur Radius:": "Weichzeichner-Radius:",
"Blur Tool": "Unschärfewerkzeug",
"Blur power:": "Weichzeichner-Stärke:",
"Borders": "Grenzen",
"Bottom": "Unterseite",
"Bottom to Top": "Unten nach oben",
"Bounds:": "Grenzen:",
"Box": "Box",
"Box Blur": "Box Unschärfe",
"Box blur": "Box Unschärfe",
"Brightness": "Helligkeit",
"Brightness:": "Helligkeit:",
"Bulge\/Pinch Tool": "Ausbuchtungs- \/ Quetschwerkzeug",
"Burn": "Brennen",
"Can not animate 1 layer.": "1 Ebene kann nicht animiert werden.",
"Can not find previous layer.": "Die vorherige Ebene kann nicht gefunden werden.",
"Can not use this tool on current layer: image already takes all area.": "Dieses Werkzeug kann auf der aktuellen Ebene nicht verwendet werden: Das Bild nimmt bereits den gesamten Bereich ein.",
"Cancel": "Abbrechen",
"Canvas Size": "Leinwandgröße",
"Center": "Zentrum",
"Center x:": "Mitte x:",
"Center y:": "Mitte y:",
"Center:": "Zentrum:",
"Change Composition": "Zusammensetzung ändern",
"Change Layer Details": "Layerdetails ändern",
"Change Opacity": "Deckkraft ändern",
"Channel:": "Kanal:",
"Circle": "Kreis",
"Clarendon": "Clarendon",
"Clear": "Löschen",
"Clear Selection": "Auswahl löschen",
"Clone Tool": "Klon-Tool",
"Clone count:": "Klonanzahl:",
"Clone tool disabled for resized image. Please rasterize first.": "Das Klon-Tool ist für das in der Größe geänderte Bild deaktiviert. Bitte zuerst rastern.",
"Cloned edges": "Klonierte Kanten",
"Close": "Schließen",
"Color #": "Farbe #",
"Color Corrections": "Farbkorrekturen",
"Color Palette": "Farbpalette",
"Color Zoom": "Farbzoom",
"Color alpha value can not be zero.": "Farb-Alpha-Wert kann nicht Null sein.",
"Color to Alpha": "Farbe zu Alpha",
"Color zoom": "Farbzoom",
"Color:": "Farbe:",
"Colors": "Farben",
"Colors:": "Farben:",
"Common Filters": "Allgemeine Filter",
"Composition": "Zusammensetzung",
"Composition:": "Zusammensetzung:",
"Content Fill": "Inhalt ausfüllen",
"Contrast": "Kontrast",
"Contrast:": "Kontrast:",
"Convert layer to raster": "Konvertieren Sie die Ebene in ein Raster",
"Convert to Raster": "In Raster konvertieren",
"Copy Selection": "Auswahl kopieren",
"Copy to Clipboard": "In die Zwischenablage kopieren",
"Courier": "Kurier",
"Crop Tool": "Freistellungswerkzeug",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Das Zuschneiden auf einer gedrehten Ebene wird nicht unterstützt. Konvertieren Sie es in Raster, um fortzufahren.",
"Ctrl + C": "Strg + C",
"Ctrl+A": "Strg + A.",
"Ctrl+C": "Strg + C.",
"Ctrl+P": "Strg+P",
"Ctrl+V": "Strg + V",
"Ctrl+Y": "Strg + Y.",
"Ctrl+Z": "Strg + Z.",
"Current": "Aktuell",
"Current Color Preview": "Aktuelle Farbvorschau",
"Custom": "Individuell",
"Data URL": "Daten-URL",
"Data URL:": "Daten-URL:",
"Decrease": "Verringern",
"Decrease Color Depth": "Farbtiefe verringern",
"Degree:": "Grad:",
"Del": "Del",
"Delete": "Löschen",
"Delete Selection": "Auswahl löschen",
"Denoise": "Denoise",
"Desaturate Tool": "Entsättigtes Werkzeug",
"Description:": "Beschreibung:",
"Deutsch": "Deutsch",
"Differences": "Unterschiede",
"Differences Down": "Unterschiede nach unten",
"Direction:": "Richtung:",
"Dither": "Dither",
"Dithering:": "Dithering:",
"Dominant color:": "Dominierende Farbe:",
"Dot Screen": "Punkt-Bildschirm",
"Down": "Runter",
"Duplicate": "Duplikat",
"Duplicate Layer": "Ebene duplizieren",
"Duplicate layer": "Ebene duplizieren",
"Dynamic": "Dynamisch",
"Edge": "Kante",
"Edit": "Bearbeiten",
"Edit text...": "Text bearbeiten...",
"Effect browser": "Effektbrowser",
"Effects": "Filter",
"Effects browser": "Effektbrowser",
"Email:": "Email:",
"Emboss": "Prägen",
"Empty selection": "Leere Auswahl",
"Empty selection or type not image.": "Leere Auswahl oder kein Bildtyp.",
"Enable autoresize:": "Automatische Größenänderung aktivieren:",
"End": "Ende",
"English": "Englisch",
"English (UK)": "Englisch UK)",
"Enrich": "Bereichern",
"Enter": "Eingeben",
"Erase Tool": "Löschwerkzeug",
"Erase on rotate object is disabled. Please rasterize first.": "„Löschen beim Drehen des Objekts“ ist deaktiviert. Bitte zuerst rastern.",
"Error": "Fehler",
"Error connecting to service.": "Fehler beim Verbinden mit dem Dienst.",
"Error loading the list of fonts from Google.": "Fehler beim Laden der Schriftartenliste von Google.",
"Error registering service worker": "Fehler beim Registrieren des Servicemitarbeiters",
"Error: can not find filter:": "Fehler: Filter kann nicht gefunden werden:",
"Error: can not find layer with id:": "Fehler: Layer mit ID kann nicht gefunden werden:",
"Error: missing details event target": "Fehler: Details zum Ereignis fehlen",
"Error: unknown layer type:": "Fehler: unbekannter Layertyp:",
"Error: unsupported attribute type:": "Fehler: nicht unterstützter Attributtyp:",
"Esc": "Esc",
"Escape": "Flucht",
"Español": "Spanisch",
"Expand edges": "Kanten erweitern",
"Exponent:": "Exponent:",
"Export": "Export",
"External": "Extern",
"Factor:": "Faktor:",
"File": "Datei",
"File name:": "Dateiname:",
"File size:": "Dateigröße:",
"Fill": "Füllen",
"Fill Tool": "Füllwerkzeug",
"Fit": "Passen",
"Fit Window": "Fenster einpassen",
"Fit window": "Fenster einbauen",
"Flatten Image": "Zu einer Ebene vereinigen",
"Flip": "Spiegeln",
"FloydSteinberg-serpentine": "FloydSteinberg-Serpentin",
"Font": "Schriftart",
"Français": "Français",
"Full HD, 1080p": "Volles HD, 1080p",
"Full Screen": "Ganzer Bildschirm",
"Full layers data": "Vollständige Layer-Daten",
"Gap:": "Spalt:",
"Gaussian Blur": "Gaußscher Weichzeichner",
"Gif delay:": "Gif Verzögerung:",
"Gingham": "Gingham",
"GitHub:": "GitHub:",
"Gradient Radius:": "Gradient Radius:",
"Grains": "Körner",
"Graphics Interchange Format": "Grafikaustauschformat",
"Gray": "Grau",
"Grayscale": "Graustufen",
"Greek": "griechisch",
"Green": "Grün",
"Green channel:": "Grüner Kanal:",
"Greyscale:": "Graustufen:",
"Grid": "Raster",
"Grid on\/off": "Raster ein \/ aus",
"Guides": "Führer",
"Guides enabled.": "Anleitungen aktiviert.",
"H Radius:": "H Radius:",
"H. Align:": "H. Ausrichten:",
"Heatmap": "Heatmap",
"Height (%):": "Höhe (%):",
"Height:": "Höhe:",
"Help": "Hilfe",
"Helvetica": "Helvetica",
"Hermite": "Hermite",
"Hex": "Verhexen",
"Hide": "Verstecken",
"Histogram": "Histogramm",
"Histogram:": "Histogramm:",
"Home": "Zuhause",
"Horizontal": "Horizontal",
"Horizontal Alignment": "Horizontale Ausrichtung",
"Horizontal blur:": "Horizontale Unschärfe:",
"Horizontal:": "Horizontal:",
"Hue": "Farbton",
"Hue Rotate": "Farbton drehen",
"Hue:": "Farbton:",
"Image": "Bild",
"Image data with multi-layers. Can be opened using miniPaint -": "Bilddaten mit mehreren Ebenen. Kann mit miniPaint geöffnet werden -",
"Impact": "Auswirkung",
"In proportion:": "Im Verhältnis:",
"Increase": "Erhöhen, ansteigen",
"Information": "Information",
"Inkwell": "Tintenfass",
"Insert": "Einfügen",
"Insert guides": "Führungen einfügen",
"Insert new layer": "Neue Ebene einfügen",
"Instagram Filters": "Instagram Filter",
"Invalid Hex Code": "Ungültiger Hex-Code",
"Italiano": "Italienisch",
"JPG\/JPEG Format": "JPG \/ JPEG-Format",
"Kerning:": "Kerning:",
"Key-Points": "Schlüsselpunkte",
"KeyU": "KeyU",
"Keyboard Shortcuts": "Tastatürkürzel",
"Keyword:": "Stichwort:",
"Lanczos": "Lanczos",
"Landscape": "Landschaft",
"Language": "Sprache",
"Last modified": "Zuletzt bearbeitet",
"Layer": "Schicht",
"Layer details": "Ebenendetails",
"Layer is empty.": "Die Ebene ist leer.",
"Layer is not compatible with resize": "Die Ebene ist nicht mit der Größenänderung kompatibel",
"Layer is vector, convert it to raster to apply this tool.": "Die Ebene ist ein Vektor. Konvertieren Sie sie in ein Raster, um dieses Werkzeug anzuwenden.",
"Layers": "Ebenen",
"Layers:": "Ebenen:",
"Layout:": "Layout:",
"Left": "Links",
"Left to Right": "Links nach rechts",
"Level:": "Niveau:",
"Levels:": "Stufen:",
"Lietuvių": "Litauisch",
"Lo-fi": "Lo-Fi",
"Luminance:": "Leuchtdichte:",
"Luminosity": "Helligkeit",
"Magic Eraser Tool": "Magic Eraser Tool",
"Merge Down": "Nach unten vereinigen",
"Merge Layers": "Ebenen zusammenführen",
"Merged": "Zusammengeführt",
"Metrics": "Metriken",
"Middle": "Mitte",
"Missing at least 1 size parameter.": "Mindestens 1 Größenparameter fehlt.",
"Missing permissions to write to Clipboard.cc": "Fehlende Berechtigungen zum Schreiben in Clipboard.cc",
"Mode:": "Modus:",
"Module function not found.": "Modulfunktion nicht gefunden.",
"Modules class not found:": "Modulklasse nicht gefunden:",
"Monospace": "Monospace",
"Mosaic": "Mosaik",
"Mouse:": "Maus:",
"Move": "Bewegung",
"Move Layer": "Ebene verschieben",
"Move layer down": "Ebene nach unten verschieben",
"Move layer up": "Ebene nach oben verschieben",
"Name:": "Name:",
"Negative": "Negativ",
"New": "Neu",
"New Bezier Layer": "Neue Bezier-Ebene",
"New Brush Layer": "Neue Pinselschicht",
"New Ellipse Layer": "Neue Ellipsenebene",
"New File": "Neue Datei",
"New Gradient Layer": "Neue Verlaufsebene",
"New Layer": "Neue Schicht",
"New Line Layer": "Neue Linienebene",
"New Pencil Layer": "Neue Bleistiftebene",
"New Polygon Layer": "Neue Polygonebene",
"New Rectangle Layer": "Neue Rechteckschicht",
"New Text Layer": "Neue Textebene",
"New file": "Neue Datei",
"New from Selection": "Neu von Auswahl",
"New layer": "Neue Ebene",
"Next": "Nächste",
"Night Vision": "Nachtsicht",
"None": "Keiner",
"Nothing is selected.": "Nichts ausgewählt.",
"Offset X:": "Offset X:",
"Offset Y:": "Offset Y:",
"Oil": "Öl",
"Ok": "OK",
"Online image editor.": "Online Bildbearbeitung.",
"Opacity": "Opazität",
"Opacity:": "Opazität:",
"Open": "Öffnen",
"Open Data URL": "Öffnen Sie die Daten-URL",
"Open Directory": "Verzeichnis öffnen",
"Open File": "Datei öffnen",
"Open File Data URL": "Öffnen Sie die Dateidaten-URL",
"Open File URL": "Öffnen Sie die Datei-URL",
"Open File Webcam": "Öffnen Sie die Datei-Webcam",
"Open Image": "Bild öffnen",
"Open JSON File": "Öffnen Sie die JSON-Datei",
"Open Test Template": "Öffnen Sie die Testvorlage",
"Open URL": "Öffne URL",
"Open data URL": "Öffnen Sie die Daten-URL",
"Open from Webcam": "Von der Webcam öffnen",
"Original Size": "Originalgröße",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Bild in SVG konvertieren",
"PageDown": "Bild nach unten",
"PageUp": "PageUp",
"Palette": "Palette",
"Parameter #1:": "Parameter # 1:",
"Parameter #2:": "Parameter # 2:",
"Paste": "Einfügen",
"Pencil": "Bleistift",
"Percentage:": "Prozentsatz:",
"Pixels:": "Pixel:",
"Placeholder comment for color channels": "Platzhalterkommentar für Farbkanäle",
"Placeholder comment for color picker": "Platzhalterkommentar für Farbwähler",
"Placeholder comment for color swatches": "Platzhalterkommentar für Farbfelder",
"Portable Network Graphics": "Tragbare Netzwerkgrafiken",
"Portrait": "Porträt",
"Português": "Português",
"Position:": "Position:",
"Power:": "Leistung:",
"Preview": "Vorschau",
"Previous": "Bisherige",
"Previous layer must be image, convert it to raster to apply this tool.": "Die vorherige Ebene muss ein Bild sein, wandeln Sie sie in ein Raster um, um dieses Werkzeug anzuwenden.",
"Print": "Drucken",
"Quality:": "Qualität:",
"Quick Load": "Schnell laden",
"Quick Save": "Schnellspeichern",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Bildhintergrund entfernen",
"Radial": "Radial",
"Radial gradient": "Radialer Verlauf",
"Radius:": "Radius:",
"Range:": "Angebot:",
"Red": "Rot",
"Red channel:": "Roter Kanal:",
"Redo": "Wiederholen",
"Remove all": "Alles entfernen",
"Rename": "Umbenennen",
"Rename Layer": "Ebene umbenennen",
"Rendered with errors.": "Mit Fehlern gerendert.",
"Rendering...": "Rendern ...",
"Replace Color": "Farbe ersetzen",
"Replace color": "Farbe ersetzen",
"Replacement:": "Ersatz:",
"Report Issues": "Probleme melden",
"Reset": "Zurücksetzen",
"Resize": "Größe ändern",
"Resize Boundary": "Größe der Grenze ändern",
"Resize Layer": "Ändern Sie die Größe der Ebene",
"Resize Layers": "Ändern Sie die Größe von Ebenen",
"Resize Text Layer": "Ändern Sie die Größe der Textebene",
"Resized as background": "Größe als Hintergrund",
"Resized:": "Größe geändert:",
"Resolution:": "Auflösung:",
"Restore Alpha": "Alpha wiederherstellen",
"Right": "Recht",
"Right angle:": "Rechter Winkel:",
"Right to Left": "Rechts nach links",
"Rotate": "Drehen",
"Rotate Layer": "Ebene drehen",
"Rotate is not supported on this type of object. Convert to raster?": "Drehen wird bei diesem Objekttyp nicht unterstützt. In Raster konvertieren?",
"Rotate left": "Nach links drehen",
"Rotate:": "Drehen:",
"Ruler": "Herrscher",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - Bilder komprimieren und vergleichen",
"Saturate": "Sättigen",
"Saturation": "Sättigung",
"Saturation:": "Sättigung:",
"Save As": "Speichern als",
"Save As Data URL": "Als Daten-URL speichern",
"Save as": "Speichern als",
"Save as type:": "Speichern unter:",
"Save layers:": "Ebenen speichern:",
"Scaling up is not supported in Hermite, using Lanczos.": "Das Skalieren wird in Hermite mit Lanczos nicht unterstützt.",
"Scroll down": "Runterscrollen",
"Scroll up": "Hochscrollen",
"Search": "Suche",
"Search Images": "Bilder suchen",
"Search for Font": "Suchen Sie nach Schriftart",
"Search:": "Suchen:",
"Select All": "Alles auswählen",
"Select Text Layer": "Wählen Sie Textebene",
"Select object tool": "Wählen Sie das Objektwerkzeug aus",
"Selected": "Ausgewählt",
"Selection Tool": "Auswahlwerkzeug",
"Sensitivity:": "Empfindlichkeit:",
"Separated": "Getrennt",
"Separated (original types)": "Getrennt (Originaltypen)",
"Sepia": "Sepia",
"Set Image Size": "Stellen Sie die Bildgröße ein",
"Settings": "Einstellungen",
"Shadow": "Schatten",
"Shapes": "Formen",
"Shapes (H)": "Formen (H)",
"Sharpen": "Schärfen",
"Sharpen Tool": "Werkzeug schärfen",
"Sharpen:": "Schärfen:",
"Shift + S": "Umschalt + S",
"Shortcut Key:": "Tastenkürzel:",
"Show": "Zeigen",
"Show \/ Hide": "Anzeigen Ausblenden",
"Show file size:": "Dateigröße anzeigen:",
"Simple": "Einfach",
"Size is too big, max": "Größe ist zu groß, max",
"Size:": "Größe:",
"Skip - layer must be image.": "Überspringen - Ebene muss ein Bild sein.",
"Solarize": "Solarisieren",
"Sorry, cold not load getUserMedia() data:": "Sorry, kalt getUserMedia () Daten nicht laden:",
"Sorry, image could not be loaded.": "Das Bild konnte leider nicht geladen werden.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Entschuldigung, Bild konnte nicht geladen werden. Versuchen Sie, das Bild zu kopieren und einzufügen.",
"Sorry, image is too big, max 5 MB.": "Entschuldigung, das Bild ist zu groß, maximal 5 MB.",
"Source coordinates saved.": "Quellkoordinaten gespeichert.",
"Source is empty, right click on image or use long press to save source position.": "Quelle ist leer, klicken Sie mit der rechten Maustaste auf das Bild oder drücken Sie lange, um die Position der Quelle zu speichern.",
"Sprites": "Sprites",
"Square": "Rechteck",
"Stream:": "Strom:",
"Strength:": "Stärke:",
"Strict": "Streng",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - Komprimiert PNG und JPEG",
"Tab": "Tab",
"Tag Image File Format": "Markieren Sie das Bilddateiformat",
"Tahoma": "Tahoma",
"Target:": "Ziel:",
"The quick brown fox jumps over the lazy dog.": "Der schnelle Braunfuchs springt über den faulen Hund.",
"There": "Dort",
"There are no layers behind.": "Es gibt keine Ebenen dahinter.",
"There is only 1 layer.": "Es gibt nur 1 Ebene.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Diese Ebene muss ein Bild enthalten. Bitte konvertieren Sie es in ein Raster, um dieses Tool anzuwenden.",
"Tilt Shift": "Neigung Verschiebung",
"Times New Roman": "Times New Roman",
"Toaster": "Toaster",
"Toggle": "Umschalten",
"Toggle Color Channels": "Farbkanäle umschalten",
"Toggle Color Picker": "Farbwähler umschalten",
"Toggle Menu": "Menü umschalten",
"Toggle Swatches": "Farbfelder umschalten",
"Tools": "Werkzeuge",
"Top": "oben",
"Top to Bottom": "Oben nach unten",
"Total pixels:": "Gesamtpixel:",
"Translate": "Übersetzen",
"Translate Layer": "Ebene übersetzen",
"Translate error, can not find dictionary:": "Fehler beim Übersetzen, Wörterbuch nicht gefunden:",
"Transparent:": "Transparent:",
"Trim": "Trimmen",
"Trim Layers": "Schichten schneiden",
"Trim borders:": "Rand schneiden:",
"Trim layer:": "Trim-Ebene:",
"Trim white color?": "Trim weiße Farbe?",
"Type:": "Typ:",
"Türkçe": "Türkçe",
"Undo": "Rückgängig machen",
"Unique colors:": "Einzigartige Farben:",
"Up": "Oben",
"Update": "Aktualisieren",
"Update Brush Layer": "Pinselebene aktualisieren",
"Update Pencil Layer": "Bleistiftebene aktualisieren",
"Update guides": "Update-Anleitungen",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Verwenden Sie die Tastenkombination Strg + V zum Einfügen aus der Zwischenablage.",
"V Radius:": "V-Radius:",
"V. Align:": "V. Ausrichten:",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "Ausführung:",
"Vertical": "Vertikal",
"Vertical Alignment": "Vertikale Ausrichtung",
"Vertical blur:": "Vertikale Unschärfe:",
"Vertical:": "Vertikal:",
"Vibrance": "Dynamik",
"View": "Sicht",
"Vignette": "Vignette",
"ViliusL": "ViliusL",
"Vintage": "Vintage",
"Webcam": "Webcam",
"Webcam #": "Webcam #",
"Website:": "Webseite:",
"Weppy File Format": "Weppy Dateiformat",
"Width (%):": "Breite (%):",
"Width:": "Breite:",
"Windows Bitmap": "Windows Bitmap",
"Word": "Wort",
"Word + Letter": "Wort + Brief",
"Wrap At:": "Wrap At:",
"Wrap:": "Wickeln:",
"Wrong dimensions": "Falsche Abmessungen",
"Wrong file type, must be image or json.": "Falscher Dateityp, muss image oder json sein.",
"X end:": "X Ende:",
"X position:": "X-Position:",
"X start:": "X Start:",
"X-Pro II": "X-Pro II",
"Y end:": "Y Ende:",
"Y position:": "Y-Position:",
"Y start:": "Y Start:",
"You can also drag and drop items into browser.": "Sie können Objekte auch per Drag & Drop in den Browser ziehen.",
"Your browser does not support canvas or JavaScript is not enabled.": "Ihr Browser unterstützt kein Canvas oder JavaScript ist nicht aktiviert.",
"Your browser does not support this format.": "Ihr Browser unterstützt dieses Format nicht.",
"Your search did not match any images.": "Ihre Suche hat keine Bilder gefunden.",
"Zoom": "Zoomen",
"Zoom Blur": "Zoom-Unschärfe",
"Zoom In": "Hineinzoomen",
"Zoom Out": "Herauszoomen",
"Zoom blur": "Zoom-Unschärfe",
"Zoom in": "Hineinzoomen",
"Zoom out": "Herauszoomen",
"Zoom:": "Zoomen:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Προέκυψε ένα πρόβλημα κατά την αφαίρεση του ιστορικού αναίρεσης",
"About": "Σχετικά",
"Active": "Ενεργό",
"Aden": "Άντεν",
"Advanced": "Προχωρημένα",
"All": "Όλα",
"Alpha": "Άλφα",
"Alpha:": "Άλφα:",
"Anonymous": "Ανώνυμο",
"Anti aliasing": "Εξομάλυνση",
"Application markup may have changed,": "Η σήμανση της εφαρμογής μπορεί να έχει αλλάξει,",
"Arial": "Άριαλ",
"Arrow": "Βέλος",
"ArrowDown": "Κάτω βέλος",
"ArrowLeft": "Αριστερό βέλος",
"ArrowRight": "Δεξί βέλος",
"ArrowUp": "Πάνω βέλος",
"Author:": "Δημιουργός",
"Auto Adjust Colors": "Αυτόματη ρύθμιση χρωμάτων",
"Auto Kerning": "Αυτόματο διάστημα χαρακτήρων",
"Average:": "Μέσο",
"Backspace": "Οπισθοδρόμηση",
"Base": "Βάση",
"Basic": "Βασικό",
"Black and White": "Μαύρο και Άσπρο",
"Blue": "Μπλε",
"Blue channel:": "Μπλε κανάλι",
"Blueprint": "αποτύπωμα",
"Blur Radius:": "Ακτίνα θολούρας",
"Blur Tool": "Εργαλείο θολούρας",
"Blur power:": "Δύναμη θολούρας",
"Borders": "Όρια",
"Bottom": "Κάτω",
"Bottom to Top": "Κάτω προς πάνω",
"Bounds:": "Περιορισμοί",
"Box": "Κουτί",
"Box Blur": "Κουτί θόλωσης",
"Box blur": "Κουτί θόλωσης",
"Brightness": "Φωτεινότητα",
"Brightness:": "Φωτεινότητα",
"Bulge\/Pinch Tool": "Εργαλείο εξογκώματος \/ Τσιμπήματος",
"Burn": "Καίω",
"Can not animate 1 layer.": "Δεν μπορεί να αναπαράγει ένα επίπεδο",
"Can not find previous layer.": "Δεν μπορεί να βρεί το προηγούμενο επίπεδο",
"Can not use this tool on current layer: image already takes all area.": "Δεν είναι δυνατή η χρήση αυτού του εργαλείου στο τρέχον επίπεδο: η εικόνα καταλαμβάνει ήδη όλη την περιοχή.",
"Cancel": "Ακύρωση",
"Canvas Size": "Μέγεθος καμβά",
"Center": "Κέντρο",
"Center x:": "Κέντρο Χ",
"Center y:": "Κέντρο Υ",
"Center:": "Κέντρο",
"Change Composition": "Αλλαγή Σύνθεσης",
"Change Layer Details": "Λεπτομέρειες αλλαγής επιπέδου",
"Change Opacity": "Αλλαγή αδιαφάνειας",
"Channel:": "Κανάλια",
"Circle": "Κύκλος",
"Clarendon": "Κλαρεντόν",
"Clear": "Καθαρισμός",
"Clear Selection": "Καθαρισμός επιλογής",
"Clone Tool": "Εργαλείο κλωνοποίησης",
"Clone count:": "Μετρητής Κλώνων",
"Clone tool disabled for resized image. Please rasterize first.": "Το εργαλείο κλωνοποίησης απενεργοποιήθηκε για αλλαγή μεγέθους εικόνας. Παρακαλώ ραστεροποιήστε πρώτα.",
"Cloned edges": "Άκρες κλώνου",
"Close": "Κλείσε",
"Color #": "Χρώμα #",
"Color Corrections": "Διορθώσεις χρώματος",
"Color Palette": "Παλέτα χρώματος",
"Color Zoom": "Εστίαση χρώματος",
"Color alpha value can not be zero.": "Η τιμή ΑΛΦΑ στο χρώμα δεν μπορεί να είναι μηδέν",
"Color to Alpha": "Χρώμα σε ΑΛΦΑ",
"Color zoom": "Εστίαση χρώματος",
"Color:": "Χρώμα",
"Colors": "Χρώματα",
"Colors:": "Χρώματα",
"Common Filters": "Κοινά φίλτρα",
"Composition": "Σύνθεση",
"Composition:": "Σύνθεση",
"Content Fill": "Γέμισμα περιεχομένου",
"Contrast": "Αντίθεση",
"Contrast:": "Αντίθεση",
"Convert layer to raster": "Μετατροπή στρώματος σε ράστερ",
"Convert to Raster": "Μετατροπή σε πίνακα τιμών",
"Copy Selection": "Αντιγραφή επιλογής",
"Copy to Clipboard": "Αντιγραφή στο πρόχειρο",
"Courier": "Μεταφορέας",
"Crop Tool": "Εργαλείο αποκοπής",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Η αποκοπή σε περιστραμμένο επίπεδο δεν υποστηρίζεται. Μετατρέψτε το σε πίνακα τιμών για να συνεχίσετε",
"Ctrl + C": "Ctrl + C",
"Ctrl+A": "Ctrl+A",
"Ctrl+C": "Ctrl+C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl+V",
"Ctrl+Y": "Ctrl+Y",
"Ctrl+Z": "Ctrl+Z",
"Current": "Τρέχον",
"Current Color Preview": "Τρέχουσα προεπισκόπηση χρώματος",
"Custom": "Προεπιλεγμένο",
"Data URL": "Δεδομένα URL",
"Data URL:": "Δεδομένα URL",
"Decrease": "Μείωση",
"Decrease Color Depth": "Μείωση βάθους χρώματος",
"Degree:": "Βαθμός",
"Del": "Διαγρ",
"Delete": "Διαγραφή",
"Delete Selection": "Διαγραφή επιλογής",
"Denoise": "Αφαίρεση θορύβου",
"Desaturate Tool": "Εργαλείο αποκορεσμού",
"Description:": "Περιγραφή",
"Deutsch": "Γερμανικά",
"Differences": "Διαφορές",
"Differences Down": "Διαφορές κάτω",
"Direction:": "Κατεύθυνση",
"Dither": "Μείωση παραμόρφωσης σήματος χαμηλού πλάτους",
"Dithering:": "Μείωση παραμόρφωσης σήματος χαμηλού πλάτους",
"Dominant color:": "Κυρίαρχο χρώμα:",
"Dot Screen": "Στίγμα οθόνης",
"Down": "Κάτω",
"Duplicate": "Διπλασίασε",
"Duplicate Layer": "Διπλασίασε επίπεδο",
"Duplicate layer": "Διπλότυπο στρώμα",
"Dynamic": "Δυναμικό",
"Edge": "Αιχμή",
"Edit": "Επεξεργασία",
"Edit text...": "Επεξεργασία κειμένου",
"Effect browser": "Κατάλογος εφέ",
"Effects": "εφέ",
"Effects browser": "Κατάλογος εφέ",
"Email:": "μέιλ",
"Emboss": "Στάμπα",
"Empty selection": "Κενή επιλογή",
"Empty selection or type not image.": "Κενή επιλογή ή όχι τύπος εικόνας",
"Enable autoresize:": "Ενεργοποίηση αυτόματου μεγέθους:",
"End": "Τέλος",
"English": "Αγγλικά",
"English (UK)": "Αγγλικά (Ηνωμένο Βασίλειο)",
"Enrich": "Εμπλουτισμός",
"Enter": "Εισαγωγή",
"Erase Tool": "Εργαλείο διαγραφής",
"Erase on rotate object is disabled. Please rasterize first.": "Η διαγραφή στο περιστρεφόμενο αντικείμενο είναι απενεργοποιημένη. Παρακαλώ ραστεροποιήστε πρώτα.",
"Error": "Σφάλμα",
"Error connecting to service.": "Σφάλμα σύνδεσης σε υπηρεσία",
"Error loading the list of fonts from Google.": "Σφάλμα κατά τη φόρτωση της λίστας γραμματοσειρών από την Google.",
"Error registering service worker": "Σφάλμα εγγραφής σε υπηρεσία",
"Error: can not find filter:": "Σφάλμα: Δεν βρίσκεται το φίλτρο",
"Error: can not find layer with id:": "Σφάλμα: Δεν βρίσκεται το επίπεδο",
"Error: missing details event target": "Σφάλμα: Λείπουν λεπτομέρειες στόχου γεγονότων",
"Error: unknown layer type:": "Σφάλμα: Άγνωστος τύπος επιπέδου",
"Error: unsupported attribute type:": "Σφάλμα: μη υποστηριζόμενος τύπος χαρακτηριστικού:",
"Esc": "Διαφ",
"Escape": "Διαφυγή",
"Español": "Ισπανικά",
"Expand edges": "Διεύρυνση άκρων",
"Exponent:": "Εκθέτης:Ν",
"Export": "Εξαγωγή",
"External": "Εξωτερικός",
"Factor:": "Παράγοντας",
"File": "Αρχείο",
"File name:": "Όνομα αρχείου",
"File size:": "Μέγεθος Αρχείου",
"Fill": "Γέμισμα",
"Fill Tool": "Εργαλείο Γεμίσματος",
"Fit": "Ταίριασμα",
"Fit Window": "Ταίριασμα στο παράθυρο",
"Fit window": "Κατάλληλο παράθυρο",
"Flatten Image": "Επιπεδοποίηση εικόνας",
"Flip": "Αναποδογύρισμα",
"FloydSteinberg-serpentine": "Σερπατίνα Φλόυντ - Στάινμπεργκ",
"Font": "Γραμματοσειρά",
"Français": "Γαλλικά",
"Full HD, 1080p": "Υψηλή ανάλυση 1080ρ",
"Full Screen": "Πλήρης Οθόνη",
"Full layers data": "Πλήρη δεδομένα επιπέδου",
"Gap:": "Κενό",
"Gaussian Blur": "Γκαουσσιανή Θόλωση",
"Gif delay:": "Καθυστέρηση gif",
"Gingham": "Gingham",
"GitHub:": "Github",
"Gradient Radius:": "Ακτίνα κλίσης",
"Grains": "Κόκκοι",
"Graphics Interchange Format": "Μορφή Μεταβαλλόμενων Γραφικών",
"Gray": "Γκρι",
"Grayscale": "Κλίμακα του Γκρι",
"Greek": "Ελληνικά",
"Green": "Πράσινο",
"Green channel:": "Πράσινο Κανάλι",
"Greyscale:": "Κλίμακα του Γκρι",
"Grid": "Πλέγμα",
"Grid on\/off": "Πλέγμα ανοικτό \/ κλειστό",
"Guides": "Οδηγοί",
"Guides enabled.": "Οδηγοί ενεργοί",
"H Radius:": "Οριζόντια Ακτίνα",
"H. Align:": "Οριζόντια Ευθυγράμμιση",
"Heatmap": "Χάρτης θερμότητας",
"Height (%):": "Ύψος (%)",
"Height:": "Ύψος",
"Help": "Βοήθεια",
"Helvetica": "Ελβετικά",
"Hermite": "Ερμητιανό",
"Hex": "Δεκαεξαδικό",
"Hide": "Κρύβω",
"Histogram": "Ιστόγραμμα",
"Histogram:": "Ιστόγραμμα",
"Home": "Αρχική",
"Horizontal": "Οριζόντιο",
"Horizontal Alignment": "Οριζόντια ευθυγράμμιση",
"Horizontal blur:": "Οριζόντια Θόλωση",
"Horizontal:": "Οριζόντιο",
"Hue": "Απόχρωση",
"Hue Rotate": "Περιστροφή απόχρωσης",
"Hue:": "Απόχρωση",
"Image": "Εικόνα",
"Image data with multi-layers. Can be opened using miniPaint -": "Δεδομένα εικόνας με πολλά επίπεδα. Δεν μπορεί να ανοιχτεί με το minipaint",
"Impact": "Επίδραση",
"In proportion:": "Σε αναλογία:",
"Increase": "Αύξηση",
"Information": "Πληροφορίες",
"Inkwell": "Πηγή μελανιού",
"Insert": "Εισαγωγή",
"Insert guides": "Οδηγοί εισαγωγής",
"Insert new layer": "Εισαγάγετε νέο στρώμα",
"Instagram Filters": "Φίλτρα ίνσταγκραμ",
"Invalid Hex Code": "Άκυρος δεκαεξαδικός κωδικός",
"Italiano": "Ιταλικά",
"JPG\/JPEG Format": "Μορφή JPG \/ JPEG",
"Kerning:": "Διάστημα χαρακτήρων",
"Key-Points": "Σημεία - κλειδί",
"KeyU": "Κλειδί υ",
"Keyboard Shortcuts": "Συντομεύσεις πληκτρολογίου",
"Keyword:": "Λέξη - κλειδί",
"Lanczos": "Ζώνη Γλώσσας",
"Landscape": "Τοπίο",
"Language": "Γλώσσα",
"Last modified": "Τελευταία τροποποίηση",
"Layer": "Επίπεδο",
"Layer details": "Λεπτομέρειες επιπέδου",
"Layer is empty.": "Το επίπεδο είναι κενό.",
"Layer is not compatible with resize": "Επίπεδο μη συμβατό με αλλαγή μεγέθους",
"Layer is vector, convert it to raster to apply this tool.": "Το επίπεδο είναι διάνυσμα. Μετατροπή πρώτα σε πίνακα, για εφαρμογή με αυτό το εργαλείο.",
"Layers": "Επίπεδα",
"Layers:": "Επίπεδα:",
"Layout:": "Διάταξη:",
"Left": "Αριστερά",
"Left to Right": "Αριστερά προς δεξιά",
"Level:": "Επίπεδο",
"Levels:": "Επίπεδα",
"Lietuvių": "Lietuviu",
"Lo-fi": "Χαμηλής συχνότητας",
"Luminance:": "Φωτισμός",
"Luminosity": "Ψωτεινότητα",
"Magic Eraser Tool": "Εργαλείο μαγικής σβήστρας",
"Merge Down": "Συγχώνευση προς τα κάτω",
"Merge Layers": "Συγχώνευση επιπέδων",
"Merged": "Συγχωνευμένος",
"Metrics": "Μετρικό",
"Middle": "Μέσαίο",
"Missing at least 1 size parameter.": "Λείπει τουλάχιστον μία παράμετρος μεγέθους",
"Missing permissions to write to Clipboard.cc": "Δεν επιτρέπεται η εγγραφή στο αρχείο πρόχειρου",
"Mode:": "Λειτουργία",
"Module function not found.": "Δεν βρέθηκε η λειτουργία της προσθήκης",
"Modules class not found:": "Δεν βρέθηκε η κλάση της προσθήκης",
"Monospace": "Μονοδιάστημα",
"Mosaic": "Μωσαικό",
"Mouse:": "Ποντίκι",
"Move": "Μετακίνησε",
"Move Layer": "Επίπεδο μετακίνησης",
"Move layer down": "Μετακινήστε το στρώμα προς τα κάτω",
"Move layer up": "Μετακινήστε το στρώμα προς τα πάνω",
"Name:": "Όνομα",
"Negative": "Αρνιτικό",
"New": "Νέο",
"New Bezier Layer": "Νέο στρώμα Bezier",
"New Brush Layer": "Νέο επίπεδο πινέλου",
"New Ellipse Layer": "Νέο επίπεδο έλλειψης",
"New File": "Νέο αρχείο",
"New Gradient Layer": "Νέο επίπεδο κλίσης",
"New Layer": "Νέο επίπεδο",
"New Line Layer": "Νέο επίπεδο γραμμής",
"New Pencil Layer": "Νέο επίπεδο μολυβιού",
"New Polygon Layer": "Νέο στρώμα πολυγώνου",
"New Rectangle Layer": "Νέο επίπεδο ορθογωνίου",
"New Text Layer": "Νέο επίπεδο κειμένου",
"New file": "Νέο αρχείο",
"New from Selection": "Νέο από επιλογή...",
"New layer": "Νέο επίπεδο",
"Next": "Επόμενο",
"Night Vision": "Νυχτερινή όραση",
"None": "Κανένα",
"Nothing is selected.": "Δεν επιλέχθηκε τίποτα",
"Offset X:": "Αντιστάθμισμα Χ",
"Offset Y:": "Αντιστάθμισμα Υ",
"Oil": "Λάδι",
"Ok": "ΟΚ",
"Online image editor.": "Διαδικτυακός Επεξεργαστής εικόνας",
"Opacity": "Αδιαφάνεια",
"Opacity:": "Αδιαφανές",
"Open": "Άνοιγμα",
"Open Data URL": "Άνοιγμα URL δεδομένων",
"Open Directory": "Άνοιγμα καταλόγου",
"Open File": "Άνοιγμα αρχείου",
"Open File Data URL": "Άνοιγμα URL αρχείου δεδομένων",
"Open File URL": "Άνοιγμα URL αρχείου",
"Open File Webcam": "Άνοιγμα αρχείου από κάμερα",
"Open Image": "Άνοιγμα εικόνας",
"Open JSON File": "Άνοιγμα αρχείου JSON",
"Open Test Template": "Άνοιγμα Δοκιμαστικού Υποδείγματος",
"Open URL": "Άνοιγμα URL",
"Open data URL": "Άνοιγμα URL δεδομένων",
"Open from Webcam": "Άνοιγμα από κάμερα",
"Original Size": "Αρχικό μέγεθος",
"PNGTOSVG - Convert Image to SVG": "Μετατροπή εικόνας από PNG σε SVG",
"PageDown": "Σελίδα παρακάτω",
"PageUp": "Σελίδα παραπάνω",
"Palette": "Παλέτα",
"Parameter #1:": "Παράμετρος #1",
"Parameter #2:": "Παράμετρος #2",
"Paste": "Επικόλληση",
"Pencil": "Μολύβι",
"Percentage:": "Ποσοστό:",
"Pixels:": "Πίξελ",
"Placeholder comment for color channels": "Σχόλιο Κατόχου για κανάλια χρωμάτων",
"Placeholder comment for color picker": "Σχόλιο Κατόχου για επιλογέα χρωμάτων",
"Placeholder comment for color swatches": "Σχόλιο Κατόχου για δείγματα χρωμάτων",
"Portable Network Graphics": "Γραφικά φορητού δικτύου",
"Portrait": "Πορτρέτο",
"Português": "Πορτογαλικά",
"Position:": "Θέση",
"Power:": "Δύναμη",
"Preview": "Προεπισκόπηση",
"Previous": "Προηγούμενο",
"Previous layer must be image, convert it to raster to apply this tool.": "Το προηγούμενο επίπεδο πρέπει να είναι εικόνα. Μετατρέψτε το σε πίνακα, για να εφαρμοστεί αυτό το εργαλείο",
"Print": "Εκτύπωση",
"Quality:": "Ποιότητα",
"Quick Load": "Γρήγορο φόρτωμα",
"Quick Save": "Γρήγορη αποθήκευση",
"REMOVE.BG - Remove Image Background": "Αφαίρεση φόντου εικόνας",
"Radial": "Ακτινικό",
"Radial gradient": "Ακτινική κλίση",
"Radius:": "Ακτίνα",
"Range:": "Εύρος",
"Red": "Κόκκινο",
"Red channel:": "Κόκκινο κανάλι",
"Redo": "Επανάλαβε",
"Remove all": "Αφαίρεσε τα όλα",
"Rename": "Μετονομασία",
"Rename Layer": "Μετονομασία επιπέδου",
"Rendered with errors.": "Διεκπεραιώθηκε με σφάλματα",
"Rendering...": "Διεκπεραίωση...",
"Replace Color": "Αντικατάσταση χρώματος",
"Replace color": "Αντικατάσταση χρώματος",
"Replacement:": "Αντικατάσταση",
"Report Issues": "Αναφορά προβλημάτων",
"Reset": "Επαναφορά",
"Resize": "Αλλαγή μεγέθους",
"Resize Boundary": "Αλλαγή μεγέθους ορίων",
"Resize Layer": "Αλλαγή μεγέθους επιπέδου",
"Resize Layers": "Αλλαγή μεγέθους επιπέδων",
"Resize Text Layer": "Αλλαγή μεγέθους επιπέδου κειμένου",
"Resized as background": "Αλλαγή μεγέθους ως φόντο",
"Resized:": "Αλλαγή μεγέθους:",
"Resolution:": "Ανάλυση",
"Restore Alpha": "Επαναφορά τιμής ΑΛΦΑ",
"Right": "Δεξιά",
"Right angle:": "Ορθή γωνία",
"Right to Left": "Δεξιά προς αριστερά",
"Rotate": "Περιστροφή",
"Rotate Layer": "Επίπεδο περιστροφής",
"Rotate is not supported on this type of object. Convert to raster?": "Η περιστροφή δεν υποστηρίζεται σε αυτού του τύπου αντικείμενο. Μετατροπή σε πίνακα;",
"Rotate left": "Περιστροφή αριστερά",
"Rotate:": "Περιστροφή",
"Ruler": "Χάρακας",
"SQUOOSH - Compress and Compare Images": "Συμπίεση και σύγκριση εικόνων",
"Saturate": "Κορεσμός",
"Saturation": "Κορεσμός",
"Saturation:": "Κορεσμός",
"Save As": "Αποθήκευση ως",
"Save As Data URL": "Αποθήκευση ως δεδομένα URL",
"Save as": "Αποθήκευση ως...",
"Save as type:": "Αποθήκευση ως τύπος...",
"Save layers:": "Αποθήκευση επιπέδων",
"Scaling up is not supported in Hermite, using Lanczos.": "Η κλιμάκωση δεν υποστηρίζεται σε ερμητιανό πίνακα, χρησιμοποιόντας LancZos",
"Scroll down": "Κύλιση κάτω",
"Scroll up": "Κύλιση πάνω",
"Search": "Αναζήτηση",
"Search Images": "Αναζήτηση εικόνων",
"Search for Font": "Αναζήτηση γραμματοσειράς",
"Search:": "Αναζήτηση:",
"Select All": "Επιλογή όλων",
"Select Text Layer": "Επιλογή επιπέδου κειμένου",
"Select object tool": "Επιλογή εργαλειου αντικειμένου",
"Selected": "Επιλεγμένο",
"Selection Tool": "Εργαλείο επιλογής ",
"Sensitivity:": "Ευαισθησία",
"Separated": "Διαχωρισμένο",
"Separated (original types)": "Διαχωρισμένοι (πρωτότυποι τύποι)",
"Sepia": "Σέπια",
"Set Image Size": "Θέσε μέγεθος εικόνας",
"Settings": "Ρυθμίσεις",
"Shadow": "Σκιά",
"Shapes": "Σχήματα",
"Shapes (H)": "Σχήματα (Η)",
"Sharpen": "Όξυνση",
"Sharpen Tool": "Εργαλείο όξυνσης",
"Sharpen:": "Όξυνση",
"Shift + S": "Shift + S",
"Shortcut Key:": "Πλήκτρο συντόμευσης",
"Show": "προβολή",
"Show \/ Hide": "Εμφάνισε \/ Κρύψε",
"Show file size:": "Δείξε μέγεθος αρχείου",
"Simple": "Απλό",
"Size is too big, max": "Μέγεθος πέρα του μέγιστου επιτρεπτού",
"Size:": "Μέγεθος",
"Skip - layer must be image.": "Παράλειψη - Το επίπεδο πρέπει να είναι εικόνα",
"Solarize": "Ηλίαση",
"Sorry, cold not load getUserMedia() data:": "Λυπάμαι, δεν μπορώ να φορτώσω τα δεδομένα",
"Sorry, image could not be loaded.": "Λυπάμαι, η εικόνα δεν μπόρεσε να φορτωθεί",
"Sorry, image could not be loaded. Try copy image and paste it.": "Λυπάμαι, η εικόνα δεν μπόρεσε να φορτωθεί. Δοκιμάστε αντιγραφή - επικόλληση.",
"Sorry, image is too big, max 5 MB.": "Λυπάμαι. Πολύ μεγάλη εικόνα. Μέγιστο μέγεθος 5 ΜΒ",
"Source coordinates saved.": "Αποθηκεύτηκαν οι συντεταγμένες της πηγής.",
"Source is empty, right click on image or use long press to save source position.": "Η πηγή είναι άδεια. Κάντε δεξί κλικ στην εικόνα ή πατήστε το παρατεταμένα για να αποθηκεύσετε την θέση της πηγής.",
"Sprites": "Αντικείμενα.",
"Square": "Τετράγωνο",
"Stream:": "Ροή",
"Strength:": "Δύναμη",
"Strict": "Περιορισμός",
"TINYPNG - Compress PNG and JPEG": "Συμπίεση PNG και JPEG",
"Tab": "Στηλοθέτης",
"Tag Image File Format": "Μορφή αρχείου εικόνας",
"Tahoma": "Ταχόμα",
"Target:": "Στόχος",
"The quick brown fox jumps over the lazy dog.": "Η γρήγορη καφέ αλεπού πηδάει πάνω από το τεμπέλικο σκυλί.",
"There": "Εκεί",
"There are no layers behind.": "Δεν υπάρχουν επίπεδα από πίσω",
"There is only 1 layer.": "Υπάρχει μόνο ένα επίπεδο",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Αυτό το επίπεδο πρέπει να περιέχει μια εικόνα. Παρακαλώ μετατρέψτε την σε πίνακα για να εφαρμόσετε αυτό το εργαλέιο.",
"Tilt Shift": "Μετατόπιση κλίσης",
"Times New Roman": "Τimes New Roman",
"Toaster": "Τοστιέρα",
"Toggle": "Εναλλαγή",
"Toggle Color Channels": "Εναλλαγή καναλιών χρώματος",
"Toggle Color Picker": "Εναλλαγή διαλογέα χρώματος",
"Toggle Menu": "Εναλλαγή στο μενού",
"Toggle Swatches": "Εναλλαγή δειγμάτων",
"Tools": "Εργαλεία",
"Top": "Κορυφή",
"Top to Bottom": "Από πάνω προς τα κάτω",
"Total pixels:": "Συνολικά πίξελς",
"Translate": "Μετάφρασε",
"Translate Layer": "Μετάφρασε το επίπεδο",
"Translate error, can not find dictionary:": "Σφάλμα μετάφρασης. Δεν βρίσκεται (σ)το λεξικό ",
"Transparent:": "Διαφανές",
"Trim": "Κούρεμα",
"Trim Layers": "Κούρεμα επιπέδων",
"Trim borders:": "Κούρεμα ορίων",
"Trim layer:": "Κούρεμα επιπέδου",
"Trim white color?": "Κούρεμα λευκού χρώματος;",
"Type:": "Τύπος",
"Türkçe": "Τούρκικα",
"Undo": "Αναίρεση",
"Unique colors:": "Μοναδικά χρώματα",
"Up": "Πάνω",
"Update": "Ενημέρωση",
"Update Brush Layer": "Ενημέρωση επιπέδου πινέλου",
"Update Pencil Layer": "Ενημέρωση επιπέδου μολυβιού",
"Update guides": "Ενημέρωση οδηγιών",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Χρησιμοποίησε τη συντόμευση Ctrl+V για να επικολλήσεις από το πρόχειρο.",
"V Radius:": "Κατακόρυφη ακτίνα",
"V. Align:": "Κατακόρυφη ευθυγράμμιση",
"Valencia": "Βαλένθια",
"Verdana": "Βερντάνα",
"Version:": "Έκδοση",
"Vertical": "Κατακόρυφο",
"Vertical Alignment": "Κατακόρυφη ευθυγράμμιση",
"Vertical blur:": "Κατακόρυφη Θόλωση",
"Vertical:": "Κατακόρυφη",
"Vibrance": "Δόνηση",
"View": "Επισκόπηση",
"Vignette": "Βινιέτα",
"ViliusL": "ViliusL",
"Vintage": "Παλιομοδίτικο",
"Webcam": "Κάμερα",
"Webcam #": "Κάμερα #",
"Website:": "Ιστότοπος",
"Weppy File Format": "Μορφή αρχείου",
"Width (%):": "Πλάτος (%)",
"Width:": "Πλάτος",
"Windows Bitmap": "Windows bitmap (χάρτης bit)",
"Word": "Λέξη",
"Word + Letter": "Λέξη + Γράμμα",
"Wrap At:": "Τύλιξε στο:",
"Wrap:": "Τύλιξε",
"Wrong dimensions": "Λάθος διαστάσεις",
"Wrong file type, must be image or json.": "Λάθος τύπος αρχείου. Πρέπει να είναι εικόνα ή JSON",
"X end:": "Χ τέλος",
"X position:": "Χ θέση",
"X start:": "Χ αρχή",
"X-Pro II": "Χ Προ ΙΙ",
"Y end:": "Υ τέλος",
"Y position:": "Υ θέση",
"Y start:": "Υ αρχή",
"You can also drag and drop items into browser.": "Μπορείς επίσης να σύρεις αντικείμενα μέσα στο φυλλομετρητή",
"Your browser does not support canvas or JavaScript is not enabled.": "Ο φυλλομετρητής σου δεν υποστηρίζει καμβά ή Javascript.",
"Your browser does not support this format.": "Ο φυλλομετρηρής σου δεν υποστηρίζει αυτή τη μορφή",
"Your search did not match any images.": "Η αναζήτηση σου δεν ταίριαξε με καμία εικόνα",
"Zoom": "Ζούμ (μεγένθυση - σμίκρυνση)",
"Zoom Blur": "Εστίαση Θολούρας",
"Zoom In": "Μεγένθυση",
"Zoom Out": "Σμίκρυνση",
"Zoom blur": "Εστίαση Θολούρας",
"Zoom in": "Μεγένθυση",
"Zoom out": "Σμίκρυνση",
"Zoom:": "Εστίαση (Ζούμ)"
}
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "",
"About": "",
"Active": "",
"Aden": "",
"Advanced": "",
"All": "",
"Alpha": "",
"Alpha:": "",
"Anonymous": "",
"Anti aliasing": "",
"Application markup may have changed,": "",
"Arial": "",
"Arrow": "",
"ArrowDown": "",
"ArrowLeft": "",
"ArrowRight": "",
"ArrowUp": "",
"Author:": "",
"Auto Adjust Colors": "",
"Auto Kerning": "",
"Average:": "",
"Backspace": "",
"Base": "",
"Basic": "",
"Black and White": "",
"Blue": "",
"Blue channel:": "",
"Blueprint": "",
"Blur Radius:": "",
"Blur Tool": "",
"Blur power:": "",
"Borders": "",
"Bottom": "",
"Bottom to Top": "",
"Bounds:": "",
"Box": "",
"Box Blur": "",
"Box blur": "",
"Brightness": "",
"Brightness:": "",
"Bulge\/Pinch Tool": "",
"Burn": "",
"Can not animate 1 layer.": "",
"Can not find previous layer.": "",
"Can not use this tool on current layer: image already takes all area.": "",
"Cancel": "",
"Canvas Size": "",
"Center": "",
"Center x:": "",
"Center y:": "",
"Center:": "",
"Change Composition": "",
"Change Layer Details": "",
"Change Opacity": "",
"Channel:": "",
"Circle": "",
"Clarendon": "",
"Clear": "",
"Clear Selection": "",
"Clone Tool": "",
"Clone count:": "",
"Clone tool disabled for resized image. Please rasterize first.": "",
"Cloned edges": "",
"Close": "",
"Color #": "",
"Color Corrections": "",
"Color Palette": "",
"Color Zoom": "",
"Color alpha value can not be zero.": "",
"Color to Alpha": "",
"Color zoom": "",
"Color:": "",
"Colors": "",
"Colors:": "",
"Common Filters": "",
"Composition": "",
"Composition:": "",
"Content Fill": "",
"Contrast": "",
"Contrast:": "",
"Convert layer to raster": "",
"Convert to Raster": "",
"Copy Selection": "",
"Copy to Clipboard": "",
"Courier": "",
"Crop Tool": "",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "",
"Ctrl + C": "",
"Ctrl+A": "",
"Ctrl+C": "",
"Ctrl+P": "",
"Ctrl+V": "",
"Ctrl+Y": "",
"Ctrl+Z": "",
"Current": "",
"Current Color Preview": "",
"Custom": "",
"Data URL": "",
"Data URL:": "",
"Decrease": "",
"Decrease Color Depth": "",
"Degree:": "",
"Del": "",
"Delete": "",
"Delete Selection": "",
"Denoise": "",
"Desaturate Tool": "",
"Description:": "",
"Deutsch": "",
"Differences": "",
"Differences Down": "",
"Direction:": "",
"Dither": "",
"Dithering:": "",
"Dominant color:": "",
"Dot Screen": "",
"Down": "",
"Duplicate": "",
"Duplicate Layer": "",
"Duplicate layer": "",
"Dynamic": "",
"Edge": "",
"Edit": "",
"Edit text...": "",
"Effect browser": "",
"Effects": "",
"Effects browser": "",
"Email:": "",
"Emboss": "",
"Empty selection": "",
"Empty selection or type not image.": "",
"Enable autoresize:": "",
"End": "",
"English": "",
"English (UK)": "",
"Enrich": "",
"Enter": "",
"Erase Tool": "",
"Erase on rotate object is disabled. Please rasterize first.": "",
"Error": "",
"Error connecting to service.": "",
"Error loading the list of fonts from Google.": "",
"Error registering service worker": "",
"Error: can not find filter:": "",
"Error: can not find layer with id:": "",
"Error: missing details event target": "",
"Error: unknown layer type:": "",
"Error: unsupported attribute type:": "",
"Esc": "",
"Escape": "",
"Español": "",
"Expand edges": "",
"Exponent:": "",
"Export": "",
"External": "",
"Factor:": "",
"File": "",
"File name:": "",
"File size:": "",
"Fill": "",
"Fill Tool": "",
"Fit": "",
"Fit Window": "",
"Fit window": "",
"Flatten Image": "",
"Flip": "",
"FloydSteinberg-serpentine": "",
"Font": "",
"Français": "",
"Full HD, 1080p": "",
"Full Screen": "",
"Full layers data": "",
"Gap:": "",
"Gaussian Blur": "",
"Gif delay:": "",
"Gingham": "",
"GitHub:": "",
"Gradient Radius:": "",
"Grains": "",
"Graphics Interchange Format": "",
"Gray": "",
"Grayscale": "",
"Greek": "",
"Green": "",
"Green channel:": "",
"Greyscale:": "",
"Grid": "",
"Grid on\/off": "",
"Guides": "",
"Guides enabled.": "",
"H Radius:": "",
"H. Align:": "",
"Heatmap": "",
"Height (%):": "",
"Height:": "",
"Help": "",
"Helvetica": "",
"Hermite": "",
"Hex": "",
"Hide": "",
"Histogram": "",
"Histogram:": "",
"Home": "",
"Horizontal": "",
"Horizontal Alignment": "",
"Horizontal blur:": "",
"Horizontal:": "",
"Hue": "",
"Hue Rotate": "",
"Hue:": "",
"Image": "",
"Image data with multi-layers. Can be opened using miniPaint -": "",
"Impact": "",
"In proportion:": "",
"Increase": "",
"Information": "",
"Inkwell": "",
"Insert": "",
"Insert guides": "",
"Insert new layer": "",
"Instagram Filters": "",
"Invalid Hex Code": "",
"Italiano": "",
"JPG\/JPEG Format": "",
"Kerning:": "",
"Key-Points": "",
"KeyU": "",
"Keyboard Shortcuts": "",
"Keyword:": "",
"Lanczos": "",
"Landscape": "",
"Language": "",
"Last modified": "",
"Layer": "",
"Layer details": "",
"Layer is empty.": "",
"Layer is not compatible with resize": "",
"Layer is vector, convert it to raster to apply this tool.": "",
"Layers": "",
"Layers:": "",
"Layout:": "",
"Left": "",
"Left to Right": "",
"Level:": "",
"Levels:": "",
"Lietuvių": "",
"Lo-fi": "",
"Luminance:": "",
"Luminosity": "",
"Magic Eraser Tool": "",
"Merge Down": "",
"Merge Layers": "",
"Merged": "",
"Metrics": "",
"Middle": "",
"Missing at least 1 size parameter.": "",
"Missing permissions to write to Clipboard.cc": "",
"Mode:": "",
"Module function not found.": "",
"Modules class not found:": "",
"Monospace": "",
"Mosaic": "",
"Mouse:": "",
"Move": "",
"Move Layer": "",
"Move layer down": "",
"Move layer up": "",
"Name:": "",
"Negative": "",
"New": "",
"New Bezier Layer": "",
"New Brush Layer": "",
"New Ellipse Layer": "",
"New File": "",
"New Gradient Layer": "",
"New Layer": "",
"New Line Layer": "",
"New Pencil Layer": "",
"New Polygon Layer": "",
"New Rectangle Layer": "",
"New Text Layer": "",
"New file": "",
"New from Selection": "",
"New layer": "",
"Next": "",
"Night Vision": "",
"None": "",
"Nothing is selected.": "",
"Offset X:": "",
"Offset Y:": "",
"Oil": "",
"Ok": "",
"Online image editor.": "",
"Opacity": "",
"Opacity:": "",
"Open": "",
"Open Data URL": "",
"Open Directory": "",
"Open File": "",
"Open File Data URL": "",
"Open File URL": "",
"Open File Webcam": "",
"Open Image": "",
"Open JSON File": "",
"Open Test Template": "",
"Open URL": "",
"Open data URL": "",
"Open from Webcam": "",
"Original Size": "",
"PNGTOSVG - Convert Image to SVG": "",
"PageDown": "",
"PageUp": "",
"Palette": "",
"Parameter #1:": "",
"Parameter #2:": "",
"Paste": "",
"Pencil": "",
"Percentage:": "",
"Pixels:": "",
"Placeholder comment for color channels": "",
"Placeholder comment for color picker": "",
"Placeholder comment for color swatches": "",
"Portable Network Graphics": "",
"Portrait": "",
"Português": "",
"Position:": "",
"Power:": "",
"Preview": "",
"Previous": "",
"Previous layer must be image, convert it to raster to apply this tool.": "",
"Print": "",
"Quality:": "",
"Quick Load": "",
"Quick Save": "",
"REMOVE.BG - Remove Image Background": "",
"Radial": "",
"Radial gradient": "",
"Radius:": "",
"Range:": "",
"Red": "",
"Red channel:": "",
"Redo": "",
"Remove all": "",
"Rename": "",
"Rename Layer": "",
"Rendered with errors.": "",
"Rendering...": "",
"Replace Color": "",
"Replace color": "",
"Replacement:": "",
"Report Issues": "",
"Reset": "",
"Resize": "",
"Resize Boundary": "",
"Resize Layer": "",
"Resize Layers": "",
"Resize Text Layer": "",
"Resized as background": "",
"Resized:": "",
"Resolution:": "",
"Restore Alpha": "",
"Right": "",
"Right angle:": "",
"Right to Left": "",
"Rotate": "",
"Rotate Layer": "",
"Rotate is not supported on this type of object. Convert to raster?": "",
"Rotate left": "",
"Rotate:": "",
"Ruler": "",
"SQUOOSH - Compress and Compare Images": "",
"Saturate": "",
"Saturation": "",
"Saturation:": "",
"Save As": "",
"Save As Data URL": "",
"Save as": "",
"Save as type:": "",
"Save layers:": "",
"Scaling up is not supported in Hermite, using Lanczos.": "",
"Scroll down": "",
"Scroll up": "",
"Search": "",
"Search Images": "",
"Search for Font": "",
"Search:": "",
"Select All": "",
"Select Text Layer": "",
"Select object tool": "",
"Selected": "",
"Selection Tool": "",
"Sensitivity:": "",
"Separated": "",
"Separated (original types)": "",
"Sepia": "",
"Set Image Size": "",
"Settings": "",
"Shadow": "",
"Shapes": "",
"Shapes (H)": "",
"Sharpen": "",
"Sharpen Tool": "",
"Sharpen:": "",
"Shift + S": "",
"Shortcut Key:": "",
"Show": "",
"Show \/ Hide": "",
"Show file size:": "",
"Simple": "",
"Size is too big, max": "",
"Size:": "",
"Skip - layer must be image.": "",
"Solarize": "",
"Sorry, cold not load getUserMedia() data:": "",
"Sorry, image could not be loaded.": "",
"Sorry, image could not be loaded. Try copy image and paste it.": "",
"Sorry, image is too big, max 5 MB.": "",
"Source coordinates saved.": "",
"Source is empty, right click on image or use long press to save source position.": "",
"Sprites": "",
"Square": "",
"Stream:": "",
"Strength:": "",
"Strict": "",
"TINYPNG - Compress PNG and JPEG": "",
"Tab": "",
"Tag Image File Format": "",
"Tahoma": "",
"Target:": "",
"The quick brown fox jumps over the lazy dog.": "",
"There": "",
"There are no layers behind.": "",
"There is only 1 layer.": "",
"This layer must contain an image. Please convert it to raster to apply this tool.": "",
"Tilt Shift": "",
"Times New Roman": "",
"Toaster": "",
"Toggle": "",
"Toggle Color Channels": "",
"Toggle Color Picker": "",
"Toggle Menu": "",
"Toggle Swatches": "",
"Tools": "",
"Top": "",
"Top to Bottom": "",
"Total pixels:": "",
"Translate": "",
"Translate Layer": "",
"Translate error, can not find dictionary:": "",
"Transparent:": "",
"Trim": "",
"Trim Layers": "",
"Trim borders:": "",
"Trim layer:": "",
"Trim white color?": "",
"Type:": "",
"Türkçe": "",
"Undo": "",
"Unique colors:": "",
"Up": "",
"Update": "",
"Update Brush Layer": "",
"Update Pencil Layer": "",
"Update guides": "",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "",
"V Radius:": "",
"V. Align:": "",
"Valencia": "",
"Verdana": "",
"Version:": "",
"Vertical": "",
"Vertical Alignment": "",
"Vertical blur:": "",
"Vertical:": "",
"Vibrance": "",
"View": "",
"Vignette": "",
"ViliusL": "",
"Vintage": "",
"Webcam": "",
"Webcam #": "",
"Website:": "",
"Weppy File Format": "",
"Width (%):": "",
"Width:": "",
"Windows Bitmap": "",
"Word": "",
"Word + Letter": "",
"Wrap At:": "",
"Wrap:": "",
"Wrong dimensions": "",
"Wrong file type, must be image or json.": "",
"X end:": "",
"X position:": "",
"X start:": "",
"X-Pro II": "",
"Y end:": "",
"Y position:": "",
"Y start:": "",
"You can also drag and drop items into browser.": "",
"Your browser does not support canvas or JavaScript is not enabled.": "",
"Your browser does not support this format.": "",
"Your search did not match any images.": "",
"Zoom": "",
"Zoom Blur": "",
"Zoom In": "",
"Zoom Out": "",
"Zoom blur": "",
"Zoom in": "",
"Zoom out": "",
"Zoom:": ""
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Ocurrió un problema al eliminar el historial de deshacer. Eso",
"About": "Acerca de",
"Active": "Activo",
"Aden": "Adén",
"Advanced": "Avanzado",
"All": "Todas",
"Alpha": "Alfa",
"Alpha:": "Alfa:",
"Anonymous": "Anónimo",
"Anti aliasing": "Anti aliasing",
"Application markup may have changed,": "Es posible que el marcado de la aplicación haya cambiado,",
"Arial": "Arial",
"Arrow": "Flecha",
"ArrowDown": "ArrowDown",
"ArrowLeft": "Flecha Izquierda",
"ArrowRight": "Flecha Derecha",
"ArrowUp": "Flecha arriba",
"Author:": "Autor:",
"Auto Adjust Colors": "Ajuste automático de colores",
"Auto Kerning": "Kerning automático",
"Average:": "Promedio:",
"Backspace": "Retroceso",
"Base": "Base",
"Basic": "BASIC",
"Black and White": "En blanco y negro",
"Blue": "Azul",
"Blue channel:": "Canal azul:",
"Blueprint": "Plano",
"Blur Radius:": "Blur Radio:",
"Blur Tool": "Herramienta de desenfoque",
"Blur power:": "Desenfoque de poder:",
"Borders": "Bordes",
"Bottom": "Fondo",
"Bottom to Top": "Abajo hacia arriba",
"Bounds:": "Límites:",
"Box": "Caja",
"Box Blur": "Caja de desenfoque",
"Box blur": "Caja de desenfoque",
"Brightness": "Brillo",
"Brightness:": "Brillo:",
"Bulge\/Pinch Tool": "Herramienta de abultamiento \/ pellizco",
"Burn": "Quemar",
"Can not animate 1 layer.": "No se puede animar 1 capa.",
"Can not find previous layer.": "No se puede encontrar la capa anterior.",
"Can not use this tool on current layer: image already takes all area.": "No se puede utilizar esta herramienta en la capa actual: la imagen ya ocupa toda el área.",
"Cancel": "Cancelar",
"Canvas Size": "Tamaño del lienzo",
"Center": "Centrar",
"Center x:": "Centro x:",
"Center y:": "Centro y:",
"Center:": "Centrar:",
"Change Composition": "Cambiar composición",
"Change Layer Details": "Cambiar los detalles de la capa",
"Change Opacity": "Cambiar la opacidad",
"Channel:": "Canal:",
"Circle": "Circulo",
"Clarendon": "Letras gruesas a la media",
"Clear": "Claro",
"Clear Selection": "Selección clara",
"Clone Tool": "Herramienta de clonación",
"Clone count:": "Recuento de clones",
"Clone tool disabled for resized image. Please rasterize first.": "Herramienta de clonación deshabilitada para imágenes redimensionadas. Rasterice primero.",
"Cloned edges": "Bordes clonados",
"Close": "Cerca",
"Color #": "Color #",
"Color Corrections": "Correcciones de color",
"Color Palette": "Paleta de color",
"Color Zoom": "Zoom de color",
"Color alpha value can not be zero.": "El valor alfa del color no puede ser cero.",
"Color to Alpha": "Color a alfa",
"Color zoom": "Zoom a color",
"Color:": "Color:",
"Colors": "Colores",
"Colors:": "Colores:",
"Common Filters": "Filtros comunes",
"Composition": "Composición",
"Composition:": "Composición:",
"Content Fill": "Relleno de contenido",
"Contrast": "Contraste",
"Contrast:": "Contraste:",
"Convert layer to raster": "Convertir capa a ráster",
"Convert to Raster": "Convertir a trama",
"Copy Selection": "Copiar selección",
"Copy to Clipboard": "Copiar al portapapeles",
"Courier": "mensajero",
"Crop Tool": "Herramienta de recorte",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "No se admite el recorte en la capa rotada. Conviértalo en ráster para continuar.",
"Ctrl + C": "Ctrl+C",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl + V",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "Corriente",
"Current Color Preview": "Vista previa del color actual",
"Custom": "Personalizado",
"Data URL": "URL de datos",
"Data URL:": "URL de datos:",
"Decrease": "Disminución",
"Decrease Color Depth": "Disminuir la profundidad de color",
"Degree:": "La licenciatura:",
"Del": "Del",
"Delete": "Borrar",
"Delete Selection": "Eliminar selección",
"Denoise": "Denoise",
"Desaturate Tool": "Herramienta de desaturar",
"Description:": "Descripción:",
"Deutsch": "Alemán",
"Differences": "Diferencias",
"Differences Down": "Diferencias hacia abajo",
"Direction:": "Dirección:",
"Dither": "Vacilar",
"Dithering:": "Dithering:",
"Dominant color:": "Color dominante:",
"Dot Screen": "Pantalla de puntos",
"Down": "Abajo",
"Duplicate": "Duplicar",
"Duplicate Layer": "Duplicar capa",
"Duplicate layer": "Duplicar capa",
"Dynamic": "Dinámica",
"Edge": "Borde",
"Edit": "Editar",
"Edit text...": "Editar texto...",
"Effect browser": "Navegador de efectos",
"Effects": "Efectos",
"Effects browser": "Navegador de efectos",
"Email:": "Email:",
"Emboss": "Realzar",
"Empty selection": "Selección vacía",
"Empty selection or type not image.": "Vaciar selección o escribir no imagen.",
"Enable autoresize:": "Habilitar tamaño automático:",
"End": "Fin",
"English": "Inglés",
"English (UK)": "Inglés del Reino Unido)",
"Enrich": "Enriquecer",
"Enter": "Entrar",
"Erase Tool": "Herramienta de borrado",
"Erase on rotate object is disabled. Please rasterize first.": "Borrar al rotar objeto está deshabilitado. Rasterice primero.",
"Error": "Error",
"Error connecting to service.": "Error al conectarse al servicio.",
"Error loading the list of fonts from Google.": "Error al cargar la lista de fuentes de Google.",
"Error registering service worker": "Error al registrar al trabajador del servicio",
"Error: can not find filter:": "Error: no se puede encontrar el filtro:",
"Error: can not find layer with id:": "Error: no se puede encontrar la capa con id:",
"Error: missing details event target": "Error: falta el objetivo del evento de detalles",
"Error: unknown layer type:": "Error: tipo de capa desconocido:",
"Error: unsupported attribute type:": "Error: tipo de atributo no admitido:",
"Esc": "Esc",
"Escape": "Escapar",
"Español": "English",
"Expand edges": "Expandir bordes",
"Exponent:": "Exponente:",
"Export": "Exportar",
"External": "Externo",
"Factor:": "Factor:",
"File": "Archivo",
"File name:": "Nombre del archivo:",
"File size:": "Tamaño del archivo:",
"Fill": "Llenar",
"Fill Tool": "Herramienta de relleno",
"Fit": "Ajuste",
"Fit Window": "Ajustar ventana",
"Fit window": "Ajustar ventana",
"Flatten Image": "Imagen aplanada",
"Flip": "Dar la vuelta",
"FloydSteinberg-serpentine": "FloydSteinberg-serpentina",
"Font": "Fuente",
"Français": "Français",
"Full HD, 1080p": "Full HD, 1080p",
"Full Screen": "Pantalla completa",
"Full layers data": "Datos de capas completas",
"Gap:": "Brecha:",
"Gaussian Blur": "Desenfoque gaussiano",
"Gif delay:": "Retraso Gif:",
"Gingham": "Guingán",
"GitHub:": "GitHub:",
"Gradient Radius:": "Radio de gradiente:",
"Grains": "Granos",
"Graphics Interchange Format": "formato de gráficos intercambeable",
"Gray": "gris",
"Grayscale": "Escala de grises",
"Greek": "Griego",
"Green": "Verde",
"Green channel:": "Canal verde:",
"Greyscale:": "Escala de grises:",
"Grid": "Cuadrícula",
"Grid on\/off": "Grid on \/ off",
"Guides": "Guías",
"Guides enabled.": "Guías habilitadas.",
"H Radius:": "H Radio:",
"H. Align:": "H. Alinear:",
"Heatmap": "Mapa de calor",
"Height (%):": "Altura (%):",
"Height:": "Altura:",
"Help": "Ayuda",
"Helvetica": "Helvética",
"Hermite": "Hermite",
"Hex": "Maleficio",
"Hide": "Esconder",
"Histogram": "Histograma",
"Histogram:": "Histograma:",
"Home": "Casa",
"Horizontal": "Horizontal",
"Horizontal Alignment": "Alineación horizontal",
"Horizontal blur:": "Desenfoque horizontal:",
"Horizontal:": "Horizontal:",
"Hue": "Matiz",
"Hue Rotate": "Hue Rotate",
"Hue:": "Matiz:",
"Image": "Imagen",
"Image data with multi-layers. Can be opened using miniPaint -": "Datos de imagen con varias capas. Se puede abrir usando miniPaint -",
"Impact": "Impacto",
"In proportion:": "En proporción:",
"Increase": "Incrementar",
"Information": "Información",
"Inkwell": "Tintero",
"Insert": "Insertar",
"Insert guides": "Insertar guías",
"Insert new layer": "Insertar nueva capa",
"Instagram Filters": "Filtros de Instagram",
"Invalid Hex Code": "Código hexadecimal no válido",
"Italiano": "Italiano",
"JPG\/JPEG Format": "Formato JPG \/ JPEG",
"Kerning:": "Interletrado:",
"Key-Points": "Puntos clave",
"KeyU": "ClaveU",
"Keyboard Shortcuts": "Atajos de teclado",
"Keyword:": "Palabra clave:",
"Lanczos": "Lanczos",
"Landscape": "Paisaje",
"Language": "Idioma",
"Last modified": "Última modificación",
"Layer": "Capa",
"Layer details": "Detalles de la capa",
"Layer is empty.": "La capa está vacía.",
"Layer is not compatible with resize": "La capa no es compatible con el cambio de tamaño",
"Layer is vector, convert it to raster to apply this tool.": "La capa es vectorial, conviértala en ráster para aplicar esta herramienta.",
"Layers": "Capas",
"Layers:": "Capas:",
"Layout:": "Disposición:",
"Left": "Izquierda",
"Left to Right": "De izquierda a derecha",
"Level:": "Nivel:",
"Levels:": "Niveles:",
"Lietuvių": "Lietuvių",
"Lo-fi": "Lo-fi",
"Luminance:": "Luminancia:",
"Luminosity": "Luminosidad",
"Magic Eraser Tool": "Herramienta de borrador mágico",
"Merge Down": "Fusionar",
"Merge Layers": "Fusionar capas",
"Merged": "Fusionado",
"Metrics": "Métrica",
"Middle": "Medio",
"Missing at least 1 size parameter.": "Falta al menos 1 parámetro de tamaño.",
"Missing permissions to write to Clipboard.cc": "Permisos faltantes para escribir en Clipboard.cc",
"Mode:": "Modo:",
"Module function not found.": "Función del módulo no encontrada.",
"Modules class not found:": "Clase de módulos no encontrada:",
"Monospace": "Monoespacio",
"Mosaic": "Mosaico",
"Mouse:": "Ratón:",
"Move": "Movimiento",
"Move Layer": "Mover capa",
"Move layer down": "Mover capa hacia abajo",
"Move layer up": "Mover capa hacia arriba",
"Name:": "Nombre:",
"Negative": "Negativo",
"New": "Nuevo",
"New Bezier Layer": "Nueva capa Bézier",
"New Brush Layer": "Nueva capa de pincel",
"New Ellipse Layer": "Nueva capa de elipse",
"New File": "Archivo nuevo",
"New Gradient Layer": "Nueva capa de degradado",
"New Layer": "Nueva capa",
"New Line Layer": "Nueva capa de línea",
"New Pencil Layer": "Nueva capa de lápiz",
"New Polygon Layer": "Nueva capa de polígono",
"New Rectangle Layer": "Nueva capa de rectángulo",
"New Text Layer": "Nueva capa de texto",
"New file": "Archivo nuevo",
"New from Selection": "Nuevo de la selección",
"New layer": "Nueva capa",
"Next": "Próximo",
"Night Vision": "Vision nocturna",
"None": "Ninguna",
"Nothing is selected.": "Nada está seleccionado.",
"Offset X:": "Compensación X:",
"Offset Y:": "Desplazamiento Y:",
"Oil": "Petróleo",
"Ok": "De acuerdo",
"Online image editor.": "Editor de imágenes en línea",
"Opacity": "Opacidad",
"Opacity:": "Opacidad:",
"Open": "Abierto",
"Open Data URL": "URL de datos abiertos",
"Open Directory": "Directorio abierto",
"Open File": "Abrir documento",
"Open File Data URL": "Abrir URL de datos de archivo",
"Open File URL": "Abrir URL de archivo",
"Open File Webcam": "Cámara web de archivos abiertos",
"Open Image": "Abrir imagen",
"Open JSON File": "Abrir archivo JSON",
"Open Test Template": "Plantilla de prueba abierta",
"Open URL": "URL abierta",
"Open data URL": "URL de datos abiertos",
"Open from Webcam": "Abrir desde la webcam",
"Original Size": "Tamaño original",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Convertir imagen a SVG",
"PageDown": "Página abajo",
"PageUp": "Página arriba",
"Palette": "Paleta",
"Parameter #1:": "Parámetro # 1:",
"Parameter #2:": "Parámetro # 2:",
"Paste": "Pegar",
"Pencil": "Lápiz",
"Percentage:": "Porcentaje:",
"Pixels:": "Píxeles:",
"Placeholder comment for color channels": "Comentario de marcador de posición para canales de color",
"Placeholder comment for color picker": "Comentario de marcador de posición para el selector de color",
"Placeholder comment for color swatches": "Comentario de marcador de posición para muestras de color",
"Portable Network Graphics": "Gráficos de red portátiles",
"Portrait": "Retrato",
"Português": "Português",
"Position:": "Posición:",
"Power:": "Poder:",
"Preview": "Avance",
"Previous": "Anterior",
"Previous layer must be image, convert it to raster to apply this tool.": "La capa anterior debe ser una imagen, conviértala a raster para aplicar esta herramienta.",
"Print": "Impresión",
"Quality:": "Calidad:",
"Quick Load": "Carga rápida",
"Quick Save": "Guardado rápido",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Eliminar fondo de imagen",
"Radial": "Radial",
"Radial gradient": "Gradiente radial",
"Radius:": "Radio:",
"Range:": "Distancia:",
"Red": "rojo",
"Red channel:": "Canal rojo:",
"Redo": "Rehacer",
"Remove all": "Eliminar todo",
"Rename": "Rebautizar",
"Rename Layer": "Cambiar nombre de capa",
"Rendered with errors.": "Rendido con errores.",
"Rendering...": "Representación...",
"Replace Color": "Reemplazar color",
"Replace color": "Reemplazar color",
"Replacement:": "Reemplazo:",
"Report Issues": "Informar problemas",
"Reset": "Reiniciar",
"Resize": "Cambiar el tamaño",
"Resize Boundary": "Cambiar tamaño de límite",
"Resize Layer": "Cambiar el tamaño de la capa",
"Resize Layers": "Cambiar el tamaño de las capas",
"Resize Text Layer": "Cambiar el tamaño de la capa de texto",
"Resized as background": "Redimensionado como fondo",
"Resized:": "Redimensionado:",
"Resolution:": "Resolución:",
"Restore Alpha": "Restaurar alfa",
"Right": "Derecha",
"Right angle:": "Ángulo recto:",
"Right to Left": "De derecha a izquierda",
"Rotate": "Girar",
"Rotate Layer": "Girar capa",
"Rotate is not supported on this type of object. Convert to raster?": "Girar no es compatible con este tipo de objeto. Convertir a raster?",
"Rotate left": "Girar a la izquierda",
"Rotate:": "Girar:",
"Ruler": "Gobernante",
"SQUOOSH - Compress and Compare Images": "SQUOOSH: comprime y compara imágenes",
"Saturate": "Saturar",
"Saturation": "Saturación",
"Saturation:": "Saturación:",
"Save As": "Guardar como",
"Save As Data URL": "Guardar como URL de datos",
"Save as": "Guardar como",
"Save as type:": "Guardar como tipo:",
"Save layers:": "Guardar capas:",
"Scaling up is not supported in Hermite, using Lanczos.": "Hermite no admite la ampliación mediante Lanczos.",
"Scroll down": "Desplazarse hacia abajo",
"Scroll up": "Desplazarse hacia arriba",
"Search": "Buscar",
"Search Images": "Buscar imágenes",
"Search for Font": "Buscar fuente",
"Search:": "Buscar:",
"Select All": "Seleccionar todo",
"Select Text Layer": "Seleccionar capa de texto",
"Select object tool": "Seleccionar herramienta de objeto",
"Selected": "Seleccionado",
"Selection Tool": "Herramienta de selección",
"Sensitivity:": "Sensibilidad:",
"Separated": "Apartado",
"Separated (original types)": "Separados (tipos originales)",
"Sepia": "Sepia",
"Set Image Size": "Establecer tamaño de imagen",
"Settings": "Configuraciones",
"Shadow": "Sombra",
"Shapes": "Formas",
"Shapes (H)": "Formas (H)",
"Sharpen": "Afilar",
"Sharpen Tool": "Herramienta de afilado",
"Sharpen:": "Afilar:",
"Shift + S": "Mayús + S",
"Shortcut Key:": "Tecla de acceso directo:",
"Show": "Espectáculo",
"Show \/ Hide": "Mostrar ocultar",
"Show file size:": "Mostrar tamaño de archivo:",
"Simple": "Sencillo",
"Size is too big, max": "El tamaño es demasiado grande, máximo",
"Size:": "Tamaño:",
"Skip - layer must be image.": "Omitir: la capa debe ser una imagen.",
"Solarize": "Solarizar",
"Sorry, cold not load getUserMedia() data:": "Lo sentimos, no se cargan los datos de getUserMedia () en frío:",
"Sorry, image could not be loaded.": "Lo sentimos, no se pudo cargar la imagen.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Lo sentimos, la imagen no se pudo cargar. Intenta copiar la imagen y pégala.",
"Sorry, image is too big, max 5 MB.": "Lo sentimos, la imagen es demasiado grande, máximo 5 MB.",
"Source coordinates saved.": "Se guardaron las coordenadas de origen.",
"Source is empty, right click on image or use long press to save source position.": "La fuente está vacía, haga clic con el botón derecho en la imagen o presione prolongadamente para guardar la posición de la fuente.",
"Sprites": "Sprites",
"Square": "Cuadrado",
"Stream:": "Corriente:",
"Strength:": "Fuerza:",
"Strict": "Estricto",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - Comprimir PNG y JPEG",
"Tab": "Lengüeta",
"Tag Image File Format": "Formato de archivo de imagen de etiqueta",
"Tahoma": "Tahoma",
"Target:": "Objetivo:",
"The quick brown fox jumps over the lazy dog.": "El veloz zorro marrón salta sobre el perro perezoso.",
"There": "Allí",
"There are no layers behind.": "No hay capas detrás",
"There is only 1 layer.": "Solo hay 1 capa",
"This layer must contain an image. Please convert it to raster to apply this tool.": "La capa debe ser una imagen, conviértala a raster para aplicar esta herramienta.",
"Tilt Shift": "Cambio de inclinación",
"Times New Roman": "Times New Roman",
"Toaster": "Tostadora",
"Toggle": "Palanca",
"Toggle Color Channels": "Alternar canales de color",
"Toggle Color Picker": "Alternar selector de color",
"Toggle Menu": "Alternar menú",
"Toggle Swatches": "Alternar muestras",
"Tools": "Herramientas",
"Top": "Parte superior",
"Top to Bottom": "De arriba hacia abajo",
"Total pixels:": "Píxeles totales:",
"Translate": "Traducir",
"Translate Layer": "Traducir capa",
"Translate error, can not find dictionary:": "Error de traducción, no se puede encontrar el diccionario:",
"Transparent:": "Transparente:",
"Trim": "Recortar",
"Trim Layers": "Recortar capas",
"Trim borders:": "Recortar bordes:",
"Trim layer:": "Capa de ajuste:",
"Trim white color?": "Recortar el color blanco?",
"Type:": "Tipo:",
"Türkçe": "Türkçe",
"Undo": "Deshacer",
"Unique colors:": "Colores únicos:",
"Up": "Arriba",
"Update": "Actualizar",
"Update Brush Layer": "Actualizar capa de pincel",
"Update Pencil Layer": "Actualizar capa de lápiz",
"Update guides": "Guías de actualización",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Use el atajo de teclado Ctrl + V para pegar desde el Portapapeles.",
"V Radius:": "V Radio:",
"V. Align:": "V. Alinear:",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "Versión:",
"Vertical": "Vertical",
"Vertical Alignment": "Alineamiento vertical",
"Vertical blur:": "Desenfoque vertical:",
"Vertical:": "Vertical:",
"Vibrance": "Vibrance",
"View": "Vista",
"Vignette": "Viñeta",
"ViliusL": "ViliusL",
"Vintage": "Vendimia",
"Webcam": "Cámara web",
"Webcam #": "Cámara web #",
"Website:": "Sitio web:",
"Weppy File Format": "Formato de archivo Weppy",
"Width (%):": "Ancho (%):",
"Width:": "Anchura:",
"Windows Bitmap": "Mapa de bits de Windows",
"Word": "Palabra",
"Word + Letter": "Palabra + Letra",
"Wrap At:": "Envolver en:",
"Wrap:": "Envolver:",
"Wrong dimensions": "Dimensiones incorrectas",
"Wrong file type, must be image or json.": "Tipo de archivo incorrecto, debe ser imagen o json.",
"X end:": "X final:",
"X position:": "Posición X:",
"X start:": "X inicio:",
"X-Pro II": "X-Pro II",
"Y end:": "Final de Y:",
"Y position:": "Posición Y:",
"Y start:": "Y comienza:",
"You can also drag and drop items into browser.": "También puede arrastrar y soltar elementos en el navegador.",
"Your browser does not support canvas or JavaScript is not enabled.": "Su navegador no admite lienzo o JavaScript no está habilitado.",
"Your browser does not support this format.": "Su navegador no es compatible con este formato.",
"Your search did not match any images.": "Su búsqueda no coincide con ninguna imagen.",
"Zoom": "Enfocar",
"Zoom Blur": "Desenfoque de zoom",
"Zoom In": "Acercarse",
"Zoom Out": "Disminuir el zoom",
"Zoom blur": "Borroso de zoom",
"Zoom in": "Acercarse",
"Zoom out": "Disminuir el zoom",
"Zoom:": "Enfocar:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Un problème est survenu lors de la suppression de l'historique des annulations. Il",
"About": "A propos",
"Active": "actif",
"Aden": "Aden",
"Advanced": "Avancé",
"All": "Tout",
"Alpha": "Alpha",
"Alpha:": "Alpha :",
"Anonymous": "Anonyme",
"Anti aliasing": "Anticrénelage",
"Application markup may have changed,": "Le balisage de l'application peut avoir changé,",
"Arial": "Arial",
"Arrow": "Flèche",
"ArrowDown": "Flèche vers le bas",
"ArrowLeft": "Flèche Gauche",
"ArrowRight": "FlècheDroite",
"ArrowUp": "Flèche vers le haut",
"Author:": "Auteur :",
"Auto Adjust Colors": "Ajuster automatiquement les couleurs",
"Auto Kerning": "Crénage automatique",
"Average:": "Moyenne :",
"Backspace": "Retour arrière",
"Base": "Base",
"Basic": "Basique",
"Black and White": "Noir et blanc",
"Blue": "Bleu",
"Blue channel:": "Niveau de bleu :",
"Blueprint": "Plan",
"Blur Radius:": "Rayon de floutage :",
"Blur Tool": "Outil de floutage",
"Blur power:": "Puissance de flou:",
"Borders": "Cadres",
"Bottom": "Bas",
"Bottom to Top": "De bas en haut",
"Bounds:": "Bornes:",
"Box": "Boîte",
"Box Blur": "Box Blur",
"Box blur": "Box flou",
"Brightness": "Luminosité",
"Brightness:": "Luminosité :",
"Bulge\/Pinch Tool": "Outil de renflement \/ pincement",
"Burn": "Brûler",
"Can not animate 1 layer.": "Impossible d'animer une couche.",
"Can not find previous layer.": "Impossible de trouver la couche précédente.",
"Can not use this tool on current layer: image already takes all area.": "Impossible d'utiliser cet outil sur le calque actuel : l'image occupe déjà toute la zone.",
"Cancel": "Annuler",
"Canvas Size": "Taille de la toile",
"Center": "Centre",
"Center x:": "Centrage x :",
"Center y:": "Centrage y :",
"Center:": "Centre :",
"Change Composition": "Changer la composition",
"Change Layer Details": "Modifier les détails du calque",
"Change Opacity": "Changer l'opacité",
"Channel:": "Niveau :",
"Circle": "Cercle",
"Clarendon": "Clarendon",
"Clear": "Effacer",
"Clear Selection": "Effacer la sélection",
"Clone Tool": "Outil de clonage",
"Clone count:": "Nombre de clones:",
"Clone tool disabled for resized image. Please rasterize first.": "Outil de clonage désactivé pour l'image redimensionnée. Veuillez d'abord pixelliser.",
"Cloned edges": "Bords clonés",
"Close": "Fermer",
"Color #": "Couleur #",
"Color Corrections": "Correction des couleurs",
"Color Palette": "Palette de couleurs",
"Color Zoom": "Eclat",
"Color alpha value can not be zero.": "La valeur alpha de la couleur ne peut pas être nulle.",
"Color to Alpha": "Rendre transparent",
"Color zoom": "Zoom couleur",
"Color:": "Couleur :",
"Colors": "Couleurs",
"Colors:": "Couleurs :",
"Common Filters": "Filtres communs",
"Composition": "Composition",
"Composition:": "Composition :",
"Content Fill": "Remplissage de contenu",
"Contrast": "Contraste",
"Contrast:": "Contraste :",
"Convert layer to raster": "Convertir le calque en raster",
"Convert to Raster": "Convertir en raster",
"Copy Selection": "Copier",
"Copy to Clipboard": "Copier dans le presse-papier",
"Courier": "Courier",
"Crop Tool": "Outil de recadrage",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Le recadrage sur le calque pivoté n'est pas pris en charge. Convertissez-le en raster pour continuer.",
"Ctrl + C": "Ctrl+C",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl + V",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "Actuel",
"Current Color Preview": "Aperçu de la couleur actuelle",
"Custom": "Personnalisé",
"Data URL": "URL de données",
"Data URL:": "URL de données:",
"Decrease": "Diminution",
"Decrease Color Depth": "Postériser",
"Degree:": "Degré:",
"Del": "Supp",
"Delete": "Supprimer",
"Delete Selection": "Effacer la sélection",
"Denoise": "Réduire le bruit",
"Desaturate Tool": "Outil de désaturation",
"Description:": "Description :",
"Deutsch": "Deutsch",
"Differences": "Détection des bords",
"Differences Down": "Détection des bords...",
"Direction:": "Direction:",
"Dither": "Ajouter du bruit",
"Dithering:": "Trame :",
"Dominant color:": "Couleur dominante:",
"Dot Screen": "Demi-teinte",
"Down": "Vers le bas",
"Duplicate": "Dupliquer",
"Duplicate Layer": "Dupliquer le calque",
"Duplicate layer": "Dupliquer le calque",
"Dynamic": "Dynamique",
"Edge": "Détection des bords",
"Edit": "Edition",
"Edit text...": "Éditer le texte...",
"Effect browser": "Navigateur d'effets",
"Effects": "Effets",
"Effects browser": "Navigateur d'effets",
"Email:": "Email :",
"Emboss": "Embossage",
"Empty selection": "Sélection vide",
"Empty selection or type not image.": "Sélection vide ou tapez pas d'image.",
"Enable autoresize:": "Activer le redimensionnement automatique :",
"End": "Fin",
"English": "Anglais",
"English (UK)": "Anglais Royaume-Uni)",
"Enrich": "Améliorer la netteté",
"Enter": "Entrer",
"Erase Tool": "Outil d'effacement",
"Erase on rotate object is disabled. Please rasterize first.": "L'effacement lors de la rotation de l'objet est désactivé. Veuillez d'abord pixelliser.",
"Error": "Erreur",
"Error connecting to service.": "Erreur lors de la connexion au service.",
"Error loading the list of fonts from Google.": "Erreur lors du chargement de la liste des polices de Google.",
"Error registering service worker": "Erreur lors de l'enregistrement du technicien de service",
"Error: can not find filter:": "Erreur: impossible de trouver le filtre:",
"Error: can not find layer with id:": "Erreur: impossible de trouver la couche avec l'ID:",
"Error: missing details event target": "Erreur: cible des événements manquants de détails",
"Error: unknown layer type:": "Erreur: type de couche inconnu:",
"Error: unsupported attribute type:": "Erreur : type d'attribut non pris en charge :",
"Esc": "Esc",
"Escape": "Échapper",
"Español": "Espagnol",
"Expand edges": "Développer les bords",
"Exponent:": "Exposant :",
"Export": "Exporter",
"External": "Externe",
"Factor:": "Facteur :",
"File": "Fichier",
"File name:": "Nom de fichier :",
"File size:": "Taille du fichier :",
"Fill": "Remplir",
"Fill Tool": "Outil de remplissage",
"Fit": "Fenêtre",
"Fit Window": "Remplir la fenêtre",
"Fit window": "Ajuster la fenêtre",
"Flatten Image": "Fusionner tous les calques",
"Flip": "Retourner",
"FloydSteinberg-serpentine": "FloydSteinberg-serpentine",
"Font": "Police de caractère",
"Français": "Français",
"Full HD, 1080p": "Full HD, 1080p",
"Full Screen": "Plein écran",
"Full layers data": "Données de couches complètes",
"Gap:": "Ecart :",
"Gaussian Blur": "Flou gaussien",
"Gif delay:": "Gif délai:",
"Gingham": "Vichy",
"GitHub:": "GitHub :",
"Gradient Radius:": "Rayon du dégradé :",
"Grains": "Grains",
"Graphics Interchange Format": "Format d'échange graphique",
"Gray": "Gris",
"Grayscale": "Niveaux de gris",
"Greek": "grec",
"Green": "Vert",
"Green channel:": "Niveau de vert :",
"Greyscale:": "Noir et blanc :",
"Grid": "Grille",
"Grid on\/off": "Grille activée \/ désactivée",
"Guides": "Guides",
"Guides enabled.": "Guides activés.",
"H Radius:": "Rayon H :",
"H. Align:": "H. Aligner:",
"Heatmap": "Zones chaudes",
"Height (%):": "Hauteur (%) :",
"Height:": "Hauteur :",
"Help": "Aide",
"Helvetica": "Helvetica",
"Hermite": "Hermite",
"Hex": "Hex",
"Hide": "Cacher",
"Histogram": "Histogramme",
"Histogram:": "Histogramme :",
"Home": "Accueil",
"Horizontal": "Horizontalement",
"Horizontal Alignment": "Alignement horizontal",
"Horizontal blur:": "Flou horizontal:",
"Horizontal:": "Horizontal:",
"Hue": "Teinte",
"Hue Rotate": "Hue Rotate",
"Hue:": "Teinte :",
"Image": "Image",
"Image data with multi-layers. Can be opened using miniPaint -": "Données d'image avec plusieurs couches. Peut être ouvert en utilisant miniPaint -",
"Impact": "Impact",
"In proportion:": "En proportion:",
"Increase": "Augmenter",
"Information": "Informations",
"Inkwell": "Encrier",
"Insert": "Insérer",
"Insert guides": "Insérer des guides",
"Insert new layer": "Insérer un nouveau calque",
"Instagram Filters": "Filtres Instagram",
"Invalid Hex Code": "Code hexadécimal non valide",
"Italiano": "Italien",
"JPG\/JPEG Format": "Format JPG \/ JPEG",
"Kerning:": "Crénage:",
"Key-Points": "Points clés",
"KeyU": "CléU",
"Keyboard Shortcuts": "Raccourcis clavier",
"Keyword:": "Mot-clé:",
"Lanczos": "Lanczos",
"Landscape": "Paysage",
"Language": "Langue",
"Last modified": "Dernière mise à jour",
"Layer": "Couche",
"Layer details": "Détails de la couche",
"Layer is empty.": "Le calque est vide.",
"Layer is not compatible with resize": "Le calque n'est pas compatible avec le redimensionnement",
"Layer is vector, convert it to raster to apply this tool.": "Le calque est vectoriel, convertissez-le en raster pour appliquer cet outil.",
"Layers": "Calques",
"Layers:": "Couches:",
"Layout:": "Mise en page:",
"Left": "à gauche",
"Left to Right": "De gauche à droite",
"Level:": "Niveau :",
"Levels:": "Niveau :",
"Lietuvių": "Lituanien",
"Lo-fi": "Lo-fi",
"Luminance:": "Luminance :",
"Luminosity": "Luminosité",
"Magic Eraser Tool": "Outil Gomme magique",
"Merge Down": "Fusionner avec le calque inférieur",
"Merge Layers": "Fusionner les calques",
"Merged": "Fusionné",
"Metrics": "Métrique",
"Middle": "Milieu",
"Missing at least 1 size parameter.": "Il manque au moins 1 paramètre de taille.",
"Missing permissions to write to Clipboard.cc": "Autorisations manquantes pour écrire dans Clipboard.cc",
"Mode:": "Mode :",
"Module function not found.": "Fonction du module introuvable.",
"Modules class not found:": "Classe de modules introuvable:",
"Monospace": "Monospace",
"Mosaic": "Mosaïque",
"Mouse:": "Souris :",
"Move": "Déplacer",
"Move Layer": "Déplacer le calque",
"Move layer down": "Déplacer le calque vers le bas",
"Move layer up": "Déplacer le calque vers le haut",
"Name:": "Nom :",
"Negative": "Négatif",
"New": "Nouveau...",
"New Bezier Layer": "Nouvelle couche de Bézier",
"New Brush Layer": "Nouveau calque de pinceau",
"New Ellipse Layer": "Nouveau calque Ellipse",
"New File": "Nouveau fichier",
"New Gradient Layer": "Nouveau calque de dégradé",
"New Layer": "Nouvelle Couche",
"New Line Layer": "Nouvelle couche de ligne",
"New Pencil Layer": "Nouveau calque de crayon",
"New Polygon Layer": "Nouveau calque de polygone",
"New Rectangle Layer": "Nouveau calque rectangle",
"New Text Layer": "Nouveau calque de texte",
"New file": "Nouveau fichier",
"New from Selection": "Nouveau à partir de la sélection",
"New layer": "Nouveau calque",
"Next": "Suivant",
"Night Vision": "Vision nocturne",
"None": "Aucun",
"Nothing is selected.": "Rien n'est sélectionné.",
"Offset X:": "Décalage X:",
"Offset Y:": "Décalage Y:",
"Oil": "Peinture à l'huile",
"Ok": "OK",
"Online image editor.": "Éditeur d'image en ligne.",
"Opacity": "Opacité",
"Opacity:": "Opacité:",
"Open": "Ouvrir",
"Open Data URL": "URL de données ouvertes",
"Open Directory": "Ouvrir le répertoire",
"Open File": "Fichier ouvert",
"Open File Data URL": "Ouvrir l'URL des données de fichier",
"Open File URL": "Ouvrir l'URL du fichier",
"Open File Webcam": "Ouvrir le fichier webcam",
"Open Image": "Ouvrir l'image",
"Open JSON File": "Ouvrez le fichier JSON",
"Open Test Template": "Modèle de test ouvert",
"Open URL": "Ouvrir depuis le Web",
"Open data URL": "URL de données ouvertes",
"Open from Webcam": "Ouvrir depuis la webcam",
"Original Size": "Format original",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Convertir l'image en SVG",
"PageDown": "Bas de page",
"PageUp": "Page Up",
"Palette": "Palette",
"Parameter #1:": "Paramètre n ° 1:",
"Parameter #2:": "Paramètre n ° 2:",
"Paste": "Coller",
"Pencil": "Crayon",
"Percentage:": "Pourcentage:",
"Pixels:": "Pixels :",
"Placeholder comment for color channels": "Commentaire d'espace réservé pour les canaux de couleur",
"Placeholder comment for color picker": "Commentaire d'espace réservé pour le sélecteur de couleurs",
"Placeholder comment for color swatches": "Commentaire d'espace réservé pour les échantillons de couleur",
"Portable Network Graphics": "Portable Network Graphics",
"Portrait": "Portrait",
"Português": "Português",
"Position:": "Position:",
"Power:": "<abbr title='Tolérance'>Tol.<\/abbr> :",
"Preview": "Aperçu",
"Previous": "précédent",
"Previous layer must be image, convert it to raster to apply this tool.": "La couche précédente doit être une image, la convertir en raster pour appliquer cet outil.",
"Print": "Imprimer",
"Quality:": "Qualité :",
"Quick Load": "Chargement rapide",
"Quick Save": "Sauvegarde rapide",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Supprimer l'arrière-plan de l'image",
"Radial": "Radial",
"Radial gradient": "Gradient radial",
"Radius:": "Rayon :",
"Range:": "Gamme :",
"Red": "Rouge",
"Red channel:": "Niveau de rouge :",
"Redo": "Refaire",
"Remove all": "Enlever tout",
"Rename": "Renommer",
"Rename Layer": "Renommer le calque",
"Rendered with errors.": "Rendu avec des erreurs.",
"Rendering...": "Le rendu...",
"Replace Color": "Remplacer une couleur",
"Replace color": "Remplacer une couleur",
"Replacement:": "Remplacement :",
"Report Issues": "Signaler un problème",
"Reset": "Réinitialiser",
"Resize": "Redimensionner",
"Resize Boundary": "Redimensionner la limite",
"Resize Layer": "Redimensionner le calque",
"Resize Layers": "Redimensionner les calques",
"Resize Text Layer": "Redimensionner le calque de texte",
"Resized as background": "Redimensionné comme arrière-plan",
"Resized:": "Redimensionné :",
"Resolution:": "Taille :",
"Restore Alpha": "Restaurer le niveau alpha",
"Right": "à droite",
"Right angle:": "Angle droit:",
"Right to Left": "De droite à gauche",
"Rotate": "Faire pivoter",
"Rotate Layer": "Faire pivoter le calque",
"Rotate is not supported on this type of object. Convert to raster?": "La rotation n'est pas prise en charge sur ce type d'objet. Convertir en raster?",
"Rotate left": "Faire pivoter à gauche",
"Rotate:": "Tourner:",
"Ruler": "Règle",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - Compresser et comparer des images",
"Saturate": "Saturer",
"Saturation": "Saturation",
"Saturation:": "Saturation :",
"Save As": "Enregistrer sous",
"Save As Data URL": "Enregistrer comme URL de données",
"Save as": "Enregistrer sous",
"Save as type:": "Enregistrer comme type :",
"Save layers:": "Enregistrer les calques :",
"Scaling up is not supported in Hermite, using Lanczos.": "La mise à l'échelle n'est pas prise en charge dans Hermite, à l'aide de Lanczos.",
"Scroll down": "Défiler vers le bas",
"Scroll up": "Défiler",
"Search": "Chercher",
"Search Images": "Rechercher des images",
"Search for Font": "Rechercher une police",
"Search:": "Recherche:",
"Select All": "Sélectionner tout",
"Select Text Layer": "Sélectionnez le calque de texte",
"Select object tool": "Déplacer les pixels sélectionnés",
"Selected": "Choisi",
"Selection Tool": "Outil de sélection",
"Sensitivity:": "Sensibilité :",
"Separated": "Séparé",
"Separated (original types)": "Séparé (types originaux)",
"Sepia": "Vieille photo",
"Set Image Size": "Définir la taille de l'image",
"Settings": "Paramètres",
"Shadow": "Ombre",
"Shapes": "Formes",
"Shapes (H)": "Formes (H)",
"Sharpen": "Améliorer la netteté",
"Sharpen Tool": "Outil Sharpen",
"Sharpen:": "Netteté :",
"Shift + S": "Maj + S",
"Shortcut Key:": "Touche de raccourci:",
"Show": "Montrer",
"Show \/ Hide": "Montrer \/ Cacher",
"Show file size:": "Calculer la taille du fichier :",
"Simple": "Simple",
"Size is too big, max": "La taille est trop grande, max",
"Size:": "Taille :",
"Skip - layer must be image.": "Skip-layer doit être image.",
"Solarize": "Solariser",
"Sorry, cold not load getUserMedia() data:": "Désolé, ne chargez pas les données getUserMedia () à froid:",
"Sorry, image could not be loaded.": "Désolé, l'image n'a pas pu être chargée.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Désolé, l'image n'a pas pu être chargée. Essayez de la copier dans le presse-papier et de la coller à la place.",
"Sorry, image is too big, max 5 MB.": "Désolé, l'image est trop grande (5MB max).",
"Source coordinates saved.": "Coordonnées source enregistrées.",
"Source is empty, right click on image or use long press to save source position.": "La source est vide, cliquez avec le bouton droit sur l'image ou appuyez longuement pour enregistrer la position de la source.",
"Sprites": "Sprites",
"Square": "Carré",
"Stream:": "Courant:",
"Strength:": "Force :",
"Strict": "Strict",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - Compresser PNG et JPEG",
"Tab": "Languette",
"Tag Image File Format": "Format de fichier image de balise",
"Tahoma": "Tahoma",
"Target:": "Cible :",
"The quick brown fox jumps over the lazy dog.": "Le renard brun rapide saute par-dessus le chien paresseux.",
"There": "Là",
"There are no layers behind.": "Il n'y a pas de couches derrière.",
"There is only 1 layer.": "Il n'y a qu'une seule couche.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Le calque doit être image, le convertir en raster pour appliquer cet outil.",
"Tilt Shift": "Flou artistique",
"Times New Roman": "Times New Roman",
"Toaster": "Grille-pain",
"Toggle": "Basculer",
"Toggle Color Channels": "Basculer les canaux de couleur",
"Toggle Color Picker": "Basculer le sélecteur de couleurs",
"Toggle Menu": "Basculer le menu",
"Toggle Swatches": "Basculer les nuances",
"Tools": "Outils",
"Top": "Haut",
"Top to Bottom": "De haut en bas",
"Total pixels:": "Nombre de pixels :",
"Translate": "Traduire",
"Translate Layer": "Traduire le calque",
"Translate error, can not find dictionary:": "Erreur de traduction, impossible de trouver le dictionnaire :",
"Transparent:": "Transparence :",
"Trim": "Rogner l'image",
"Trim Layers": "Couper les couches",
"Trim borders:": "Couper les bordures:",
"Trim layer:": "Couche de garniture:",
"Trim white color?": "Taillez la couleur blanche?",
"Type:": "Taper:",
"Türkçe": "Türkçe",
"Undo": "Annuler",
"Unique colors:": "Couleurs uniques :",
"Up": "Vers le haut",
"Update": "Mise à jour",
"Update Brush Layer": "Mettre à jour le calque de pinceau",
"Update Pencil Layer": "Mettre à jour le calque de crayon",
"Update guides": "Guides de mise à jour",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Utilisez le raccourci clavier Ctrl + V pour coller à partir du Presse-papiers.",
"V Radius:": "Rayon V :",
"V. Align:": "V. Aligner:",
"Valencia": "Valence",
"Verdana": "Verdana",
"Version:": "Version:",
"Vertical": "Verticalement",
"Vertical Alignment": "Alignement vertical",
"Vertical blur:": "Flou vertical:",
"Vertical:": "Verticale:",
"Vibrance": "Vibrance",
"View": "Voir",
"Vignette": "Vignette",
"ViliusL": "ViliusL",
"Vintage": "Vintage",
"Webcam": "Webcam",
"Webcam #": "Webcam #",
"Website:": "Site Internet:",
"Weppy File Format": "Format de fichier Weppy",
"Width (%):": "Largeur (%) :",
"Width:": "Largeur :",
"Windows Bitmap": "Bitmap Windows",
"Word": "Mot",
"Word + Letter": "Mot + Lettre",
"Wrap At:": "Envelopper à:",
"Wrap:": "Emballage:",
"Wrong dimensions": "Mauvaises dimensions",
"Wrong file type, must be image or json.": "Mauvais type de fichier, image ou json attendu.",
"X end:": "Fin X :",
"X position:": "Position x :",
"X start:": "Début X :",
"X-Pro II": "X-Pro II",
"Y end:": "Fin Y :",
"Y position:": "Position y :",
"Y start:": "Début Y :",
"You can also drag and drop items into browser.": "Vous pouvez également faire glisser et déposer des éléments dans le navigateur.",
"Your browser does not support canvas or JavaScript is not enabled.": "Votre navigateur ne supporte pas le canevas ou JavaScript n'est pas activé.",
"Your browser does not support this format.": "Votre navigateur ne supporte pas ce format.",
"Your search did not match any images.": "Votre recherche ne correspond à aucune image.",
"Zoom": "Zoom",
"Zoom Blur": "Zoom Flou",
"Zoom In": "Agrandir",
"Zoom Out": "Réduire",
"Zoom blur": "Zoom flou",
"Zoom in": "Zoomer",
"Zoom out": "Dézoomer",
"Zoom:": "Zoom :"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Si è verificato un problema durante la rimozione della cronologia degli annullamenti. It",
"About": "Di",
"Active": "Attivo",
"Aden": "Aden",
"Advanced": "Avanzate",
"All": "Tutti",
"Alpha": "Alfa",
"Alpha:": "Alfa:",
"Anonymous": "Anonimo",
"Anti aliasing": "Anti aliasing",
"Application markup may have changed,": "Il markup dell'applicazione potrebbe essere cambiato",
"Arial": "Arial",
"Arrow": "Freccia",
"ArrowDown": "ArrowDown",
"ArrowLeft": "ArrowLeft",
"ArrowRight": "ArrowRight",
"ArrowUp": "ArrowUp",
"Author:": "Autore:",
"Auto Adjust Colors": "Regola automaticamente i colori",
"Auto Kerning": "Crenatura automatica",
"Average:": "Media:",
"Backspace": "Backspace",
"Base": "Base",
"Basic": "Di base",
"Black and White": "Bianco e nero",
"Blue": "Blu",
"Blue channel:": "Canale blu:",
"Blueprint": "Planimetria",
"Blur Radius:": "Sfocatura raggio:",
"Blur Tool": "Strumento di sfocatura",
"Blur power:": "Sfocatura:",
"Borders": "frontiere",
"Bottom": "Parte inferiore",
"Bottom to Top": "Dal basso verso l'alto",
"Bounds:": "Limiti:",
"Box": "Scatola",
"Box Blur": "Box Blur",
"Box blur": "Scatola sfocatura",
"Brightness": "Luminosità",
"Brightness:": "Luminosità:",
"Bulge\/Pinch Tool": "Strumento rigonfiamento \/ pizzico",
"Burn": "Bruciare",
"Can not animate 1 layer.": "Impossibile animare 1 livello.",
"Can not find previous layer.": "Impossibile trovare il livello precedente.",
"Can not use this tool on current layer: image already takes all area.": "Impossibile utilizzare questo strumento sul livello corrente: l'immagine occupa già tutta l'area.",
"Cancel": "Annulla",
"Canvas Size": "Dimensioni della tela",
"Center": "Centro",
"Center x:": "Centro x:",
"Center y:": "Centro y:",
"Center:": "Centro:",
"Change Composition": "Cambia composizione",
"Change Layer Details": "Cambia i dettagli del livello",
"Change Opacity": "Cambia opacità",
"Channel:": "Canale:",
"Circle": "Cerchio",
"Clarendon": "Clarendon",
"Clear": "Chiaro",
"Clear Selection": "Cancella selezione",
"Clone Tool": "Strumento clone",
"Clone count:": "Conteggio dei cloni:",
"Clone tool disabled for resized image. Please rasterize first.": "Strumento clone disabilitato per l'immagine ridimensionata. Per favore rasterizza prima.",
"Cloned edges": "Bordi clonati",
"Close": "Vicino",
"Color #": "Colore #",
"Color Corrections": "Correzioni di colore",
"Color Palette": "Palette dei colori",
"Color Zoom": "Zoom a colori",
"Color alpha value can not be zero.": "Il valore alfa del colore non può essere zero.",
"Color to Alpha": "Colore ad alfa",
"Color zoom": "Zoom a colori",
"Color:": "Colore:",
"Colors": "Colori",
"Colors:": "Colori:",
"Common Filters": "Filtri comuni",
"Composition": "Composizione",
"Composition:": "Composizione:",
"Content Fill": "Riempimento del contenuto",
"Contrast": "Contrasto",
"Contrast:": "Contrasto:",
"Convert layer to raster": "Converti livello in raster",
"Convert to Raster": "Converti in raster",
"Copy Selection": "Copia selezione",
"Copy to Clipboard": "Copia negli appunti",
"Courier": "Corriere",
"Crop Tool": "Strumento di ritaglio",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Il ritaglio su livello ruotato non è supportato. Convertilo in raster per continuare.",
"Ctrl + C": "CTRL+C",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "CTRL+P",
"Ctrl+V": "Ctrl + V",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "attuale",
"Current Color Preview": "Anteprima colore corrente",
"Custom": "costume",
"Data URL": "URL dei dati",
"Data URL:": "URL dei dati:",
"Decrease": "Diminuire",
"Decrease Color Depth": "Diminuisci la profondità del colore",
"Degree:": "Grado:",
"Del": "del",
"Delete": "Elimina",
"Delete Selection": "Elimina selezione",
"Denoise": "Denoise",
"Desaturate Tool": "Strumento di desatura",
"Description:": "Descrizione:",
"Deutsch": "Tedesco",
"Differences": "differenze",
"Differences Down": "Differenze giù",
"Direction:": "Direzione:",
"Dither": "oscillare",
"Dithering:": "dithering:",
"Dominant color:": "Colore dominante:",
"Dot Screen": "Schermo a punti",
"Down": "Giù",
"Duplicate": "Duplicare",
"Duplicate Layer": "Livello duplicato",
"Duplicate layer": "Livello duplicato",
"Dynamic": "Dinamico",
"Edge": "Bordo",
"Edit": "modificare",
"Edit text...": "Modifica il testo...",
"Effect browser": "Browser effetti",
"Effects": "effetti",
"Effects browser": "Browser degli effetti",
"Email:": "E-mail:",
"Emboss": "rilievo",
"Empty selection": "Selezione vuota",
"Empty selection or type not image.": "Selezione vuota o tipo non immagine.",
"Enable autoresize:": "Abilita ridimensionamento automatico:",
"End": "Fine",
"English": "Inglese",
"English (UK)": "Inglese (Regno Unito)",
"Enrich": "Arricchire",
"Enter": "accedere",
"Erase Tool": "Strumento di cancellazione",
"Erase on rotate object is disabled. Please rasterize first.": "La cancellazione durante la rotazione dell'oggetto è disabilitata. Per favore rasterizza prima.",
"Error": "Errore",
"Error connecting to service.": "Errore durante la connessione al servizio.",
"Error loading the list of fonts from Google.": "Errore durante il caricamento dell'elenco dei caratteri da Google.",
"Error registering service worker": "Errore durante la registrazione dell'operatore del servizio",
"Error: can not find filter:": "Errore: impossibile trovare il filtro:",
"Error: can not find layer with id:": "Errore: impossibile trovare il livello con ID:",
"Error: missing details event target": "Errore: manca il bersaglio dell'evento dettagli",
"Error: unknown layer type:": "Errore: tipo di livello sconosciuto:",
"Error: unsupported attribute type:": "Errore: tipo di attributo non supportato:",
"Esc": "Esc",
"Escape": "Fuga",
"Español": "Español",
"Expand edges": "Espandi i bordi",
"Exponent:": "Esponente:",
"Export": "Esportare",
"External": "Esterno",
"Factor:": "Fattore:",
"File": "File",
"File name:": "Nome del file:",
"File size:": "Dimensione del file:",
"Fill": "Riempire",
"Fill Tool": "Strumento di riempimento",
"Fit": "In forma",
"Fit Window": "Finestra adatta",
"Fit window": "Adatta la finestra",
"Flatten Image": "Immagine piatta",
"Flip": "Flip",
"FloydSteinberg-serpentine": "FloydSteinberg-serpentina",
"Font": "Font",
"Français": "Français",
"Full HD, 1080p": "Full HD, 1080p",
"Full Screen": "A schermo intero",
"Full layers data": "Dati a strati completi",
"Gap:": "Gap:",
"Gaussian Blur": "Sfocatura gaussiana",
"Gif delay:": "Ritardo Gif:",
"Gingham": "Percalle",
"GitHub:": "GitHub:",
"Gradient Radius:": "Raggio di pendenza:",
"Grains": "Grani",
"Graphics Interchange Format": "Formato di interscambio grafico",
"Gray": "Grigio",
"Grayscale": "Scala di grigi",
"Greek": "greco",
"Green": "verde",
"Green channel:": "Canale Verde:",
"Greyscale:": "Scala di grigi:",
"Grid": "Griglia",
"Grid on\/off": "Griglia on \/ off",
"Guides": "Guide",
"Guides enabled.": "Guide abilitate.",
"H Radius:": "Raggio H:",
"H. Align:": "H. Allinea:",
"Heatmap": "Mappa di calore",
"Height (%):": "Altezza (%):",
"Height:": "Altezza:",
"Help": "Aiuto",
"Helvetica": "Helvetica",
"Hermite": "Hermite",
"Hex": "Esadecimale",
"Hide": "Nascondere",
"Histogram": "Istogramma",
"Histogram:": "Istogramma:",
"Home": "Casa",
"Horizontal": "Orizzontale",
"Horizontal Alignment": "Allineamento orizzontale",
"Horizontal blur:": "Sfocatura orizzontale:",
"Horizontal:": "Orizzontale:",
"Hue": "Hue",
"Hue Rotate": "Tonalità Ruota",
"Hue:": "Hue:",
"Image": "Immagine",
"Image data with multi-layers. Can be opened using miniPaint -": "Dati immagine con multi-layer. Può essere aperto usando miniPaint -",
"Impact": "urto",
"In proportion:": "In proporzione:",
"Increase": "Aumentare",
"Information": "Informazione",
"Inkwell": "Calamaio",
"Insert": "Inserire",
"Insert guides": "Inserire le guide",
"Insert new layer": "Inserisci un nuovo livello",
"Instagram Filters": "Filtri Instagram",
"Invalid Hex Code": "Codice esadecimale non valido",
"Italiano": "Italiano",
"JPG\/JPEG Format": "Formato JPG \/ JPEG",
"Kerning:": "Crenatura:",
"Key-Points": "Punti chiave",
"KeyU": "KeyU",
"Keyboard Shortcuts": "Tasti rapidi",
"Keyword:": "Parola chiave:",
"Lanczos": "Lanczos",
"Landscape": "Paesaggio",
"Language": "linguaggio",
"Last modified": "Ultima modifica",
"Layer": "Strato",
"Layer details": "Dettagli del livello",
"Layer is empty.": "Il livello è vuoto.",
"Layer is not compatible with resize": "Il livello non è compatibile con il ridimensionamento",
"Layer is vector, convert it to raster to apply this tool.": "Il livello è vettoriale, convertilo in raster per applicare questo strumento.",
"Layers": "Livelli",
"Layers:": "strati:",
"Layout:": "Disposizione:",
"Left": "Sinistra",
"Left to Right": "Da sinistra a destra",
"Level:": "Livello:",
"Levels:": "livelli:",
"Lietuvių": "Lietuvių",
"Lo-fi": "Lo-fi",
"Luminance:": "Luminance:",
"Luminosity": "Luminosità",
"Magic Eraser Tool": "Strumento gomma magica",
"Merge Down": "Unisci giù",
"Merge Layers": "Unire i livelli",
"Merged": "Fusione",
"Metrics": "Metrica",
"Middle": "Medio",
"Missing at least 1 size parameter.": "Manca almeno 1 parametro di dimensione.",
"Missing permissions to write to Clipboard.cc": "Mancano i permessi per scrivere su Clipboard.cc",
"Mode:": "Modalità:",
"Module function not found.": "Funzione del modulo non trovata.",
"Modules class not found:": "Classe di moduli non trovata:",
"Monospace": "Monospace",
"Mosaic": "Mosaico",
"Mouse:": "Topo:",
"Move": "Mossa",
"Move Layer": "Sposta livello",
"Move layer down": "Sposta il livello verso il basso",
"Move layer up": "Sposta il livello verso l'alto",
"Name:": "Nome:",
"Negative": "Negativo",
"New": "Nuovo",
"New Bezier Layer": "Nuovo livello Bezier",
"New Brush Layer": "Nuovo livello pennello",
"New Ellipse Layer": "Nuovo livello ellisse",
"New File": "Nuovo file",
"New Gradient Layer": "Nuovo livello sfumato",
"New Layer": "Nuovo strato",
"New Line Layer": "Nuovo livello di linea",
"New Pencil Layer": "Nuovo livello matita",
"New Polygon Layer": "Nuovo livello poligono",
"New Rectangle Layer": "Nuovo livello rettangolo",
"New Text Layer": "Nuovo livello di testo",
"New file": "Nuovo file",
"New from Selection": "Novità dalla selezione",
"New layer": "Nuovo strato",
"Next": "Prossimo",
"Night Vision": "Visione notturna",
"None": "Nessuna",
"Nothing is selected.": "Niente è selezionato.",
"Offset X:": "Offset X:",
"Offset Y:": "Offset Y:",
"Oil": "Olio",
"Ok": "Ok",
"Online image editor.": "Editor di immagini online",
"Opacity": "Opacità",
"Opacity:": "Opacità:",
"Open": "Aperto",
"Open Data URL": "Apri URL dati",
"Open Directory": "Apri Directory",
"Open File": "Apri il file",
"Open File Data URL": "Apri URL dati file",
"Open File URL": "Apri URL file",
"Open File Webcam": "Apri File Webcam",
"Open Image": "Apri immagine",
"Open JSON File": "Apri file JSON",
"Open Test Template": "Apri modello di prova",
"Open URL": "Apri URL",
"Open data URL": "Apri l'URL dei dati",
"Open from Webcam": "Apri da webcam",
"Original Size": "Misura originale",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Converti immagine in SVG",
"PageDown": "Pagina giù",
"PageUp": "Pagina su",
"Palette": "Tavolozza",
"Parameter #1:": "Parametro n. 1:",
"Parameter #2:": "Parametro n. 2:",
"Paste": "Incolla",
"Pencil": "Matita",
"Percentage:": "Percentuale:",
"Pixels:": "pixel:",
"Placeholder comment for color channels": "Commento segnaposto per i canali di colore",
"Placeholder comment for color picker": "Commento segnaposto per il selettore di colori",
"Placeholder comment for color swatches": "Commento segnaposto per i campioni di colore",
"Portable Network Graphics": "Grafica di rete portatile",
"Portrait": "Ritratto",
"Português": "Português",
"Position:": "Posizione:",
"Power:": "Energia:",
"Preview": "Anteprima",
"Previous": "Precedente",
"Previous layer must be image, convert it to raster to apply this tool.": "Il livello precedente deve essere un'immagine, convertirlo in raster per applicare questo strumento.",
"Print": "Stampare",
"Quality:": "Qualità:",
"Quick Load": "Carico rapido",
"Quick Save": "Salvataggio veloce",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Rimuovi lo sfondo dell'immagine",
"Radial": "Radiale",
"Radial gradient": "Gradiente radiale",
"Radius:": "Raggio:",
"Range:": "Gamma:",
"Red": "Rosso",
"Red channel:": "Canale Rosso:",
"Redo": "Rifare",
"Remove all": "Rimuovi tutto",
"Rename": "Rinominare",
"Rename Layer": "Rinomina livello",
"Rendered with errors.": "Resi con errori.",
"Rendering...": "Rendering ...",
"Replace Color": "Sostituisci colore",
"Replace color": "Sostituisci colore",
"Replacement:": "Sostituzione:",
"Report Issues": "Segnala problemi",
"Reset": "Reset",
"Resize": "Ridimensiona",
"Resize Boundary": "Ridimensiona confine",
"Resize Layer": "Ridimensiona livello",
"Resize Layers": "Ridimensiona i livelli",
"Resize Text Layer": "Ridimensiona il livello del testo",
"Resized as background": "Ridimensionato come sfondo",
"Resized:": "Ridimensionato:",
"Resolution:": "Risoluzione:",
"Restore Alpha": "Ripristina alpha",
"Right": "Destra",
"Right angle:": "Angolo retto:",
"Right to Left": "Da destra a sinistra",
"Rotate": "Ruotare",
"Rotate Layer": "Ruota livello",
"Rotate is not supported on this type of object. Convert to raster?": "Ruota non è supportato su questo tipo di oggetto. Converti in raster?",
"Rotate left": "Gira a sinistra",
"Rotate:": "Ruotare:",
"Ruler": "Governate",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - Comprimi e confronta le immagini",
"Saturate": "Saturare",
"Saturation": "Saturazione",
"Saturation:": "Saturazione:",
"Save As": "Salva come",
"Save As Data URL": "Salva come URL dei dati",
"Save as": "Salva come",
"Save as type:": "Salva come tipo:",
"Save layers:": "Salva livelli:",
"Scaling up is not supported in Hermite, using Lanczos.": "L'aumento di scala non è supportato in Hermite, utilizzando Lanczos.",
"Scroll down": "Scorri verso il basso",
"Scroll up": "Scorrere verso l'alto",
"Search": "Ricerca",
"Search Images": "Cerca immagini",
"Search for Font": "Cerca carattere",
"Search:": "Ricerca:",
"Select All": "Seleziona tutto",
"Select Text Layer": "Seleziona Livello testo",
"Select object tool": "Seleziona lo strumento oggetto",
"Selected": "Selezionato",
"Selection Tool": "Strumento di selezione",
"Sensitivity:": "sensibilità:",
"Separated": "Separato",
"Separated (original types)": "Separati (tipi originali)",
"Sepia": "nero di seppia",
"Set Image Size": "Imposta la dimensione dell'immagine",
"Settings": "impostazioni",
"Shadow": "Ombra",
"Shapes": "Forme",
"Shapes (H)": "Forme (H)",
"Sharpen": "Affilare",
"Sharpen Tool": "Strumento di nitidezza",
"Sharpen:": "Affilare:",
"Shift + S": "Maiusc+S",
"Shortcut Key:": "Tasto di scelta rapida:",
"Show": "Spettacolo",
"Show \/ Hide": "Mostra nascondi",
"Show file size:": "Mostra la dimensione del file:",
"Simple": "Semplice",
"Size is too big, max": "La dimensione è troppo grande, max",
"Size:": "Dimensione:",
"Skip - layer must be image.": "Salta: il livello deve essere un'immagine.",
"Solarize": "solarizzare",
"Sorry, cold not load getUserMedia() data:": "Spiacenti, non caricare i dati getUserMedia ():",
"Sorry, image could not be loaded.": "Spiacenti, impossibile caricare l'immagine.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Spiacenti, l'immagine non può essere caricata. Prova a copiare l'immagine e incollarla.",
"Sorry, image is too big, max 5 MB.": "Siamo spiacenti, l'immagine è troppo grande, max 5 MB.",
"Source coordinates saved.": "Coordinate di origine salvate.",
"Source is empty, right click on image or use long press to save source position.": "La sorgente è vuota, fare clic con il tasto destro sull'immagine o premere a lungo per salvare la posizione della sorgente.",
"Sprites": "sprites",
"Square": "Piazza",
"Stream:": "Stream:",
"Strength:": "Forza:",
"Strict": "Rigoroso",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - Comprimi PNG e JPEG",
"Tab": "Tab",
"Tag Image File Format": "Etichetta il formato del file immagine",
"Tahoma": "Tahoma",
"Target:": "Bersaglio:",
"The quick brown fox jumps over the lazy dog.": "La veloce volpe marrone salta sopra il cane pigro.",
"There": "Là",
"There are no layers behind.": "Non ci sono strati dietro.",
"There is only 1 layer.": "C'è solo 1 strato.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Il livello deve essere un'immagine, convertirlo in raster per applicare questo strumento.",
"Tilt Shift": "Tilt Shift",
"Times New Roman": "Times New Roman",
"Toaster": "Tostapane",
"Toggle": "Toggle",
"Toggle Color Channels": "Attiva \/ disattiva i canali colore",
"Toggle Color Picker": "Attiva \/ disattiva il selettore dei colori",
"Toggle Menu": "Toggle Menu",
"Toggle Swatches": "Attiva \/ disattiva campioni",
"Tools": "Utensili",
"Top": "Superiore",
"Top to Bottom": "Dall'alto al basso",
"Total pixels:": "Pixel totali:",
"Translate": "Tradurre",
"Translate Layer": "Traduci Layer",
"Translate error, can not find dictionary:": "Traduci errore, impossibile trovare il dizionario:",
"Transparent:": "Trasparente:",
"Trim": "tagliare",
"Trim Layers": "Livelli di taglio",
"Trim borders:": "Taglia bordi:",
"Trim layer:": "Strato di rifinitura:",
"Trim white color?": "Tagliare il colore bianco?",
"Type:": "Tipo:",
"Türkçe": "Türkçe",
"Undo": "Disfare",
"Unique colors:": "Colori unici:",
"Up": "Su",
"Update": "Aggiornamento",
"Update Brush Layer": "Aggiorna livello pennello",
"Update Pencil Layer": "Aggiorna livello matita",
"Update guides": "Guide di aggiornamento",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Usa la scorciatoia da tastiera Ctrl + V per incollare dagli Appunti.",
"V Radius:": "V raggio:",
"V. Align:": "V. Allinea:",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "Versione:",
"Vertical": "Verticale",
"Vertical Alignment": "Allineamento verticale",
"Vertical blur:": "Sfocatura verticale:",
"Vertical:": "Verticale:",
"Vibrance": "Vibrance",
"View": "Visualizzazione",
"Vignette": "vignette",
"ViliusL": "ViliusL",
"Vintage": "Vintage ▾",
"Webcam": "Webcam",
"Webcam #": "Webcam #",
"Website:": "Sito web:",
"Weppy File Format": "Formato file Weppy",
"Width (%):": "Larghezza (%):",
"Width:": "Larghezza:",
"Windows Bitmap": "Bitmap di Windows",
"Word": "parola",
"Word + Letter": "Parola + Lettera",
"Wrap At:": "Avvolgi a:",
"Wrap:": "Avvolgere:",
"Wrong dimensions": "Dimensioni sbagliate",
"Wrong file type, must be image or json.": "Tipo di file errato, deve essere immagine o json.",
"X end:": "X fine:",
"X position:": "Posizione X:",
"X start:": "X inizio:",
"X-Pro II": "X-Pro II",
"Y end:": "Fine Y:",
"Y position:": "Posizione Y:",
"Y start:": "Y inizio:",
"You can also drag and drop items into browser.": "Puoi anche trascinare gli oggetti nel browser.",
"Your browser does not support canvas or JavaScript is not enabled.": "Il tuo browser non supporta canvas o JavaScript non è abilitato.",
"Your browser does not support this format.": "Il tuo browser non supporta questo formato.",
"Your search did not match any images.": "La tua ricerca non corrisponde ad alcuna immagine.",
"Zoom": "Zoom",
"Zoom Blur": "Zoom sfocatura",
"Zoom In": "Ingrandire",
"Zoom Out": "Zoom indietro",
"Zoom blur": "Sfocatura dello zoom",
"Zoom in": "Ingrandire",
"Zoom out": "Zoom indietro",
"Zoom:": "Zoom:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "元に戻す履歴の削除中に問題が発生しました。それ",
"About": "開発者について",
"Active": "アクティブ",
"Aden": "アデン",
"Advanced": "上級",
"All": "すべて",
"Alpha": "アルファ",
"Alpha:": "アルファ:",
"Anonymous": "匿名",
"Anti aliasing": "アンチエイリアシング",
"Application markup may have changed,": "アプリケーションのマークアップが変更されている可能性があります。",
"Arial": "Arial",
"Arrow": "矢印",
"ArrowDown": "ArrowDown",
"ArrowLeft": "ArrowLeft",
"ArrowRight": "ArrowRight",
"ArrowUp": "ArrowUp",
"Author:": "著者:",
"Auto Adjust Colors": "色を自動調整する",
"Auto Kerning": "自動カーニング",
"Average:": "平均:",
"Backspace": "バックスペース",
"Base": "ベース",
"Basic": "ベーシック",
"Black and White": "黒と白",
"Blue": "青",
"Blue channel:": "ブルーチャンネル:",
"Blueprint": "青写真",
"Blur Radius:": "ぼかし半径:",
"Blur Tool": "ぼかしツール",
"Blur power:": "ぼかしパワー:",
"Borders": "罫線",
"Bottom": "下",
"Bottom to Top": "下から上へ",
"Bounds:": "境界:",
"Box": "ボックス",
"Box Blur": "ボックスのぼかし",
"Box blur": "ボックスボケ",
"Brightness": "輝度",
"Brightness:": "輝度:",
"Bulge\/Pinch Tool": "バルジ\/ピンチツール",
"Burn": "燃やす",
"Can not animate 1 layer.": "1つのレイヤーをアニメートできません。",
"Can not find previous layer.": "以前のレイヤーが見つかりません。",
"Can not use this tool on current layer: image already takes all area.": "現在のレイヤーではこのツールを使用できません: 画像がすでにすべての領域を占めています。",
"Cancel": "キャンセル",
"Canvas Size": "キャンバスサイズ",
"Center": "センター",
"Center x:": "センターx",
"Center y:": "センターy",
"Center:": "センター:",
"Change Composition": "構成を変更する",
"Change Layer Details": "レイヤーの詳細を変更する",
"Change Opacity": "不透明度を変更する",
"Channel:": "チャネル:",
"Circle": "サークル",
"Clarendon": "クラレンドン",
"Clear": "クリア",
"Clear Selection": "明確な選択",
"Clone Tool": "クローンツール",
"Clone count:": "クローン数:",
"Clone tool disabled for resized image. Please rasterize first.": "サイズ変更された画像に対してクローン ツールが無効になりました。まずはラスタライズを行ってください。",
"Cloned edges": "クローンエッジ",
"Close": "近い",
"Color #": "色 ",
"Color Corrections": "色補正",
"Color Palette": "カラーパレット",
"Color Zoom": "カラーズーム",
"Color alpha value can not be zero.": "色のアルファ値はゼロにすることはできません。",
"Color to Alpha": "カラーからアルファ",
"Color zoom": "カラーズーム",
"Color:": "色:",
"Colors": "色",
"Colors:": "色:",
"Common Filters": "共通フィルター",
"Composition": "レイヤーの合成",
"Composition:": "レイヤーの合成:",
"Content Fill": "コンテンツの埋め込み",
"Contrast": "コントラスト",
"Contrast:": "コントラスト:",
"Convert layer to raster": "レイヤーをラスターに変換",
"Convert to Raster": "ラスタに変換する",
"Copy Selection": "選択コピー",
"Copy to Clipboard": "クリップボードにコピー",
"Courier": "宅配便",
"Crop Tool": "切り抜きツール",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "回転したレイヤーでのトリミングはサポートされていません。続行するには、ラスターに変換してください。",
"Ctrl + C": "Ctrl + C",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl + V",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "現在",
"Current Color Preview": "現在のカラープレビュー",
"Custom": "カスタム",
"Data URL": "データURL",
"Data URL:": "データURL",
"Decrease": "減少",
"Decrease Color Depth": "色深度を減らす",
"Degree:": "度:",
"Del": "デル",
"Delete": "削除",
"Delete Selection": "選択を削除する",
"Denoise": "デノアーズ",
"Desaturate Tool": "彩度を下げるツール",
"Description:": "説明:",
"Deutsch": "ドイツ語",
"Differences": "相違点",
"Differences Down": "相違点",
"Direction:": "方向:",
"Dither": "ディザ",
"Dithering:": "ディザリング:",
"Dominant color:": "支配的な色:",
"Dot Screen": "ドットスクリーン",
"Down": "ダウン",
"Duplicate": "重複",
"Duplicate Layer": "重複レイヤー",
"Duplicate layer": "レイヤーの複製",
"Dynamic": "動的",
"Edge": "エッジ",
"Edit": "編集",
"Edit text...": "テキストを編集...",
"Effect browser": "エフェクトブラウザ",
"Effects": "エフェクト",
"Effects browser": "エフェクトブラウザ",
"Email:": "Eメール:",
"Emboss": "エンボス",
"Empty selection": "空の選択",
"Empty selection or type not image.": "空の選択またはタイプではない画像。",
"Enable autoresize:": "自動サイズ変更を有効にする:",
"End": "終わり",
"English": "英語",
"English (UK)": "英語(イギリス)",
"Enrich": "エンリッチ",
"Enter": "入る",
"Erase Tool": "消去ツール",
"Erase on rotate object is disabled. Please rasterize first.": "オブジェクトの回転時の消去は無効になっています。まずはラスタライズを行ってください。",
"Error": "エラー",
"Error connecting to service.": "サービスに接続中にエラーが発生しました。",
"Error loading the list of fonts from Google.": "Google からフォントのリストをロード中にエラーが発生しました。",
"Error registering service worker": "Service Worker の登録エラー",
"Error: can not find filter:": "エラー:フィルターが見つかりません:",
"Error: can not find layer with id:": "エラー:IDのレイヤーが見つかりません:",
"Error: missing details event target": "エラー:詳細イベントターゲットがありません",
"Error: unknown layer type:": "エラー:不明なレイヤータイプ:",
"Error: unsupported attribute type:": "エラー: サポートされていない属性タイプ:",
"Esc": "ESC",
"Escape": "逃れる",
"Español": "スペイン語",
"Expand edges": "エッジを開く",
"Exponent:": "指数:",
"Export": "エクスポート",
"External": "外部サイト",
"Factor:": "因子:",
"File": "ファイル",
"File name:": "ファイル名:",
"File size:": "ファイルサイズ:",
"Fill": "塗りつぶす",
"Fill Tool": "塗りつぶしツール",
"Fit": "フィット",
"Fit Window": "ウィンドウに合わせる",
"Fit window": "ウィンドウにフィット",
"Flatten Image": "画像を平ら",
"Flip": "反転",
"FloydSteinberg-serpentine": "FloydSteinberg-蛇紋文字",
"Font": "フォント",
"Français": "フランス語",
"Full HD, 1080p": "フルHD、1080p",
"Full Screen": "全画面表示",
"Full layers data": "フルレイヤーデータ",
"Gap:": "ギャップ:",
"Gaussian Blur": "ガウスぼかし",
"Gif delay:": "GIF遅延:",
"Gingham": "ギンガム",
"GitHub:": "GitHub",
"Gradient Radius:": "勾配半径:",
"Grains": "フィルムグレイン",
"Graphics Interchange Format": "グラフィック交換フォーマット",
"Gray": "グレー",
"Grayscale": "グレースケール",
"Greek": "ギリシャ語",
"Green": "緑",
"Green channel:": "グリーンチャネル:",
"Greyscale:": "グレースケール:",
"Grid": "グリッド",
"Grid on\/off": "グリッドのオン\/オフ",
"Guides": "ガイド",
"Guides enabled.": "ガイドが有効になりました。",
"H Radius:": "H半径:",
"H. Align:": "H.整列:",
"Heatmap": "ヒートマップ",
"Height (%):": "高さ (%):",
"Height:": "高さ:",
"Help": "Help",
"Helvetica": "ヘルベチカ",
"Hermite": "エルミート",
"Hex": "16進数",
"Hide": "隠れる",
"Histogram": "ヒストグラム",
"Histogram:": "ヒストグラム:",
"Home": "ホーム",
"Horizontal": "水平",
"Horizontal Alignment": "水平方向の配置",
"Horizontal blur:": "水平ブラー:",
"Horizontal:": "水平:",
"Hue": "色相",
"Hue Rotate": "色相回転",
"Hue:": "色相:",
"Image": "画像",
"Image data with multi-layers. Can be opened using miniPaint -": "マルチレイヤーの画像データ。 miniPaintを使用して開くことができます -",
"Impact": "影響",
"In proportion:": "比例して:",
"Increase": "増加する",
"Information": "情報",
"Inkwell": "インク壺",
"Insert": "追加",
"Insert guides": "インサートガイド",
"Insert new layer": "新しいレイヤーを挿入",
"Instagram Filters": "Instagramフィルター",
"Invalid Hex Code": "無効な16進コード",
"Italiano": "イタリア語",
"JPG\/JPEG Format": "JPG \/ JPEG形式",
"Kerning:": "字詰め:",
"Key-Points": "キーポイント",
"KeyU": "キーユー",
"Keyboard Shortcuts": "キーボードショートカット",
"Keyword:": "キーワード:",
"Lanczos": "ランチョス",
"Landscape": "風景",
"Language": "言語",
"Last modified": "最終更新日",
"Layer": "レイヤー",
"Layer details": "レイヤの詳細",
"Layer is empty.": "レイヤーが空です。",
"Layer is not compatible with resize": "レイヤーはサイズ変更と互換性がありません",
"Layer is vector, convert it to raster to apply this tool.": "レイヤーはベクターです。このツールを適用するには、レイヤーをラスターに変換してください。",
"Layers": "レイヤー",
"Layers:": "レイヤー:",
"Layout:": "レイアウト:",
"Left": "左",
"Left to Right": "左から右へ",
"Level:": "レベル:",
"Levels:": "レベル:",
"Lietuvių": "Lietuvių",
"Lo-fi": "ローファイ",
"Luminance:": "輝度:",
"Luminosity": "光度",
"Magic Eraser Tool": "魔法の消しゴムツール",
"Merge Down": "マージダウン",
"Merge Layers": "レイヤーをマージする",
"Merged": "合併",
"Metrics": "指標",
"Middle": "中間",
"Missing at least 1 size parameter.": "少なくとも1つのサイズパラメータがありません。",
"Missing permissions to write to Clipboard.cc": "Clipboard.ccに書き込むためのアクセス許可がありません",
"Mode:": "モード:",
"Module function not found.": "モジュール機能が見つかりません。",
"Modules class not found:": "モジュールクラスが見つかりません:",
"Monospace": "モノスペース",
"Mosaic": "モザイク",
"Mouse:": "マウス:",
"Move": "移動",
"Move Layer": "レイヤーを移動",
"Move layer down": "レイヤーを下に移動します",
"Move layer up": "レイヤーを上に移動",
"Name:": "名:",
"Negative": "負",
"New": "新しい",
"New Bezier Layer": "新しいベジェ層",
"New Brush Layer": "新しいブラシレイヤー",
"New Ellipse Layer": "新しい楕円レイヤー",
"New File": "新しいファイル",
"New Gradient Layer": "新しいグラデーションレイヤー",
"New Layer": "新しいレイヤー",
"New Line Layer": "新しいラインレイヤー",
"New Pencil Layer": "新しい鉛筆レイヤー",
"New Polygon Layer": "新しいポリゴンレイヤー",
"New Rectangle Layer": "新しい長方形レイヤー",
"New Text Layer": "新しいテキストレイヤー",
"New file": "新しいファイル",
"New from Selection": "新しい選択から",
"New layer": "新しいレイヤー",
"Next": "次",
"Night Vision": "暗視ゴーグル 緑",
"None": "なし",
"Nothing is selected.": "何も選択されていません。",
"Offset X:": "オフセットX",
"Offset Y:": "オフセットY",
"Oil": "油",
"Ok": "OK",
"Online image editor.": "オンラインイメージエディタ。",
"Opacity": "不透明度",
"Opacity:": "不透明度:",
"Open": "開く",
"Open Data URL": "公開データURL",
"Open Directory": "ディレクトリを開く",
"Open File": "ファイルを開く",
"Open File Data URL": "ファイルデータのURLを開く",
"Open File URL": "ファイルのURLを開く",
"Open File Webcam": "ファイルWebカメラを開く",
"Open Image": "画像を開く",
"Open JSON File": "JSONファイルを開く",
"Open Test Template": "テストテンプレートを開く",
"Open URL": "URLを開く",
"Open data URL": "公開データURL",
"Open from Webcam": "ウェブカメラから開く",
"Original Size": "オリジナルサイズ",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - 画像をSVGに変換",
"PageDown": "ページダウン",
"PageUp": "ページアップ",
"Palette": "パレット",
"Parameter #1:": "パラメータ#1",
"Parameter #2:": "パラメータ#2",
"Paste": "ペースト",
"Pencil": "鉛筆",
"Percentage:": "パーセンテージ:",
"Pixels:": "ピクセル:",
"Placeholder comment for color channels": "カラーチャンネルのプレースホルダーコメント",
"Placeholder comment for color picker": "カラーピッカーのプレースホルダーコメント",
"Placeholder comment for color swatches": "色見本のプレースホルダーコメント",
"Portable Network Graphics": "ポータブルネットワークグラフィックス",
"Portrait": "肖像画",
"Português": "ポルトガル語",
"Position:": "位置:",
"Power:": "力:",
"Preview": "プレビュー",
"Previous": "前",
"Previous layer must be image, convert it to raster to apply this tool.": "前のレイヤーはイメージでなければならず、このツールを適用するにはラスターに変換する必要があります。",
"Print": "印刷",
"Quality:": "品質:",
"Quick Load": "クイックロード",
"Quick Save": "クイックセーブ",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - 画像の背景を削除する",
"Radial": "ラジアル",
"Radial gradient": "放射グラジエント",
"Radius:": "半径:",
"Range:": "範囲:",
"Red": "赤",
"Red channel:": "赤いチャンネル:",
"Redo": "やり直し",
"Remove all": "すべて削除する",
"Rename": "名前を変更する",
"Rename Layer": "レイヤーの名前を変更",
"Rendered with errors.": "エラーでレンダリングされました。",
"Rendering...": "レンダリング...",
"Replace Color": "色を置き換える",
"Replace color": "色を交換する",
"Replacement:": "置換:",
"Report Issues": "レポートの問題",
"Reset": "リセット",
"Resize": "サイズを変更する",
"Resize Boundary": "境界のサイズ変更",
"Resize Layer": "レイヤーのサイズ変更",
"Resize Layers": "レイヤーのサイズ変更",
"Resize Text Layer": "テキストレイヤーのサイズ変更",
"Resized as background": "背景としてサイズ変更",
"Resized:": "サイズ変更:",
"Resolution:": "解像度(ppi)",
"Restore Alpha": "アルファを復元する",
"Right": "右",
"Right angle:": "直角:",
"Right to Left": "右から左へ",
"Rotate": "回転する",
"Rotate Layer": "レイヤーを回転",
"Rotate is not supported on this type of object. Convert to raster?": "このタイプのオブジェクトでは、回転はサポートされていません。ラスタに変換しますか?",
"Rotate left": "左に回転",
"Rotate:": "回転:",
"Ruler": "ルーラー",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - 画像を圧縮して比較する",
"Saturate": "飽和",
"Saturation": "飽和",
"Saturation:": "飽和:",
"Save As": "名前を付けて保存",
"Save As Data URL": "データURLとして保存",
"Save as": "名前を付けて保存",
"Save as type:": "タイプとして保存:",
"Save layers:": "レイヤーを保存:",
"Scaling up is not supported in Hermite, using Lanczos.": "ランチョスを使用したエルミートでは、スケールアップはサポートされていません。",
"Scroll down": "下へスクロール",
"Scroll up": "スクロールアップする",
"Search": "サーチ",
"Search Images": "画像を検索する",
"Search for Font": "フォントの検索",
"Search:": "検索:",
"Select All": "すべて選択",
"Select Text Layer": "テキストレイヤーを選択",
"Select object tool": "オブジェクトツールを選択",
"Selected": "選択された",
"Selection Tool": "選択ツール",
"Sensitivity:": "感度:",
"Separated": "分離",
"Separated (original types)": "セパレート(オリジナルタイプ)",
"Sepia": "セピア",
"Set Image Size": "画像サイズを設定する",
"Settings": "設定",
"Shadow": "影",
"Shapes": "形",
"Shapes (H)": "形状(H)",
"Sharpen": "シャープ",
"Sharpen Tool": "シャープツール",
"Sharpen:": "シャープ:",
"Shift + S": "Shift + S",
"Shortcut Key:": "ショートカットキー:",
"Show": "見せる",
"Show \/ Hide": "表示\/非表示",
"Show file size:": "ファイルサイズを表示:",
"Simple": "シンプル",
"Size is too big, max": "サイズが大きすぎます",
"Size:": "サイズ:",
"Skip - layer must be image.": "スキップ - レイヤはイメージでなければなりません。",
"Solarize": "ソラリゼーション",
"Sorry, cold not load getUserMedia() data:": "申し訳ありませんが、getUserMedia()データをロードしないでください:",
"Sorry, image could not be loaded.": "申し訳ありませんが、画像を読み込めませんでした。",
"Sorry, image could not be loaded. Try copy image and paste it.": "申し訳ありませんが、画像を読み込めませんでした。イメージをコピーして貼り付けてみてください。",
"Sorry, image is too big, max 5 MB.": "申し訳ありませんが、イメージが大きすぎます(最大5 MB)。",
"Source coordinates saved.": "保存されたソース座標。",
"Source is empty, right click on image or use long press to save source position.": "ソースが空です。画像を右クリックするか、長押ししてソースの位置を保存します。",
"Sprites": "スプライト",
"Square": "平方",
"Stream:": "ストリーム:",
"Strength:": "力:",
"Strict": "厳格",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - PNGとJPEGを圧縮します",
"Tab": "タブ",
"Tag Image File Format": "タグ画像ファイル形式",
"Tahoma": "タホマ",
"Target:": "ターゲット:",
"The quick brown fox jumps over the lazy dog.": "素早い茶色のキツネが怠惰な犬を飛び越えます。",
"There": "そこ",
"There are no layers behind.": "後ろに層がありません。",
"There is only 1 layer.": "レイヤーは1つしかありません。",
"This layer must contain an image. Please convert it to raster to apply this tool.": "レイヤはイメージでなければならず、このツールを適用するにはラスタに変換する必要があります。",
"Tilt Shift": "チルトシフト",
"Times New Roman": "Times New Roman",
"Toaster": "トースター",
"Toggle": "トグル",
"Toggle Color Channels": "カラーチャンネルを切り替えます",
"Toggle Color Picker": "トグルカラーピッカー",
"Toggle Menu": "トグルメニュー",
"Toggle Swatches": "トグルスウォッチ",
"Tools": "ツール",
"Top": "上",
"Top to Bottom": "上から下へ",
"Total pixels:": "合計ピクセル数:",
"Translate": "画像を移動",
"Translate Layer": "翻訳レイヤー",
"Translate error, can not find dictionary:": "翻訳エラー、辞書が見つかりません:",
"Transparent:": "トランスペアレント:",
"Trim": "トリム",
"Trim Layers": "レイヤーのトリム",
"Trim borders:": "境界線をトリミングします。",
"Trim layer:": "トリムレイヤー:",
"Trim white color?": "白い色をトリム?",
"Type:": "タイプ:",
"Türkçe": "Türkçe",
"Undo": "元に戻す",
"Unique colors:": "ユニークな色:",
"Up": "アップ",
"Update": "アップデート",
"Update Brush Layer": "ブラシレイヤーを更新する",
"Update Pencil Layer": "鉛筆レイヤーを更新する",
"Update guides": "アップデートガイド",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Ctrl + Vキーボードショートカットを使用してクリップボードから貼り付けます。",
"V Radius:": "V半径:",
"V. Align:": "V.整列:",
"Valencia": "バレンシア",
"Verdana": "ヴェルダナ",
"Version:": "バージョン:",
"Vertical": "垂直",
"Vertical Alignment": "垂直方向の配置",
"Vertical blur:": "垂直方向のぼかし:",
"Vertical:": "垂直:",
"Vibrance": "バイブランス",
"View": "ビュー",
"Vignette": "ビネット",
"ViliusL": "ViliusL",
"Vintage": "ビンテージ",
"Webcam": "ウェブカメラ",
"Webcam #": "ウェブカメラ ",
"Website:": "ウェブサイト:",
"Weppy File Format": "Weppyファイル形式",
"Width (%):": "幅(%):",
"Width:": "幅:",
"Windows Bitmap": "Windowsビットマップ",
"Word": "語",
"Word + Letter": "単語+文字",
"Wrap At:": "ラップ場所:",
"Wrap:": "ラップ:",
"Wrong dimensions": "間違った寸法",
"Wrong file type, must be image or json.": "間違ったファイルタイプです。画像またはjsonでなければなりません。",
"X end:": "X end",
"X position:": "X位置:",
"X start:": "Xスタート:",
"X-Pro II": "X-Pro II",
"Y end:": "Y end",
"Y position:": "Y位置:",
"Y start:": "Y開始:",
"You can also drag and drop items into browser.": "アイテムをブラウザにドラッグアンドドロップすることもできます。",
"Your browser does not support canvas or JavaScript is not enabled.": "ブラウザがキャンバスをサポートしていないか、JavaScriptが有効になっていません。",
"Your browser does not support this format.": "お使いのブラウザはこの形式をサポートしていません。",
"Your search did not match any images.": "あなたの検索はどの画像にも一致しませんでした。",
"Zoom": "ズーム",
"Zoom Blur": "ズームぼかし",
"Zoom In": "ズームイン",
"Zoom Out": "ズームアウトする",
"Zoom blur": "ズームブラー",
"Zoom in": "ズームイン",
"Zoom out": "ズームアウトする",
"Zoom:": "ズーム:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "실행 취소 기록을 제거하는 동안 문제가 발생했습니다. 그것",
"About": "약",
"Active": "유효한",
"Aden": "아덴",
"Advanced": "많은",
"All": "모든",
"Alpha": "알파",
"Alpha:": "알파 :",
"Anonymous": "익명",
"Anti aliasing": "안티 앨리어싱",
"Application markup may have changed,": "애플리케이션 마크업이 변경되었을 수 있습니다.",
"Arial": "Arial",
"Arrow": "화살",
"ArrowDown": "ArrowDown",
"ArrowLeft": "ArrowLeft",
"ArrowRight": "ArrowRight",
"ArrowUp": "ArrowUp",
"Author:": "저자:",
"Auto Adjust Colors": "색상 자동 조정",
"Auto Kerning": "자동 커닝",
"Average:": "평균:",
"Backspace": "역행 키이",
"Base": "베이스",
"Basic": "기본",
"Black and White": "검정색과 흰색",
"Blue": "푸른",
"Blue channel:": "파란색 채널 :",
"Blueprint": "청사진",
"Blur Radius:": "흐리게 반경 :",
"Blur Tool": "블러 도구",
"Blur power:": "흐림 효과 :",
"Borders": "테두리",
"Bottom": "바닥",
"Bottom to Top": "아래에서 위로",
"Bounds:": "범위:",
"Box": "상자",
"Box Blur": "상자 흐림 효과",
"Box blur": "상자 흐림 효과",
"Brightness": "명도",
"Brightness:": "명도:",
"Bulge\/Pinch Tool": "벌지 \/ 핀치 도구",
"Burn": "화상",
"Can not animate 1 layer.": "1 개의 레이어를 애니메이션으로 만들 수 없습니다.",
"Can not find previous layer.": "이전 레이어를 찾을 수 없습니다.",
"Can not use this tool on current layer: image already takes all area.": "현재 레이어에서는 이 도구를 사용할 수 없습니다. 이미지가 이미 모든 영역을 차지하고 있습니다.",
"Cancel": "취소",
"Canvas Size": "캔버스 크기",
"Center": "센터",
"Center x:": "센터 x :",
"Center y:": "센터 y :",
"Center:": "센터:",
"Change Composition": "구성 변경",
"Change Layer Details": "레이어 세부 정보 변경",
"Change Opacity": "불투명도 변경",
"Channel:": "채널:",
"Circle": "원",
"Clarendon": "Clarendon",
"Clear": "명확한",
"Clear Selection": "명확한 선택",
"Clone Tool": "복제 도구",
"Clone count:": "클론 횟수 :",
"Clone tool disabled for resized image. Please rasterize first.": "크기가 조정된 이미지에 대해 복제 도구가 비활성화되었습니다. 먼저 래스터화해 주세요.",
"Cloned edges": "복제 된 가장자리",
"Close": "닫다",
"Color #": "색깔 #",
"Color Corrections": "색상 보정",
"Color Palette": "색상 팔레트",
"Color Zoom": "색상 확대 \/ 축소",
"Color alpha value can not be zero.": "색상 알파 값은 0 일 수 없습니다.",
"Color to Alpha": "알파에서 색상으로",
"Color zoom": "색상 확대 \/ 축소",
"Color:": "색깔:",
"Colors": "그림 물감",
"Colors:": "그림 물감:",
"Common Filters": "공통 필터",
"Composition": "구성",
"Composition:": "구성:",
"Content Fill": "콘텐츠 채우기",
"Contrast": "대조",
"Contrast:": "대조:",
"Convert layer to raster": "레이어를 래스터로 변환",
"Convert to Raster": "래스터로 변환",
"Copy Selection": "선택 항목 복사",
"Copy to Clipboard": "클립 보드에 복사",
"Courier": "급사",
"Crop Tool": "자르기 도구",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "회전 된 레이어에서 자르기는 지원되지 않습니다. 계속하려면 래스터로 변환하십시오.",
"Ctrl + C": "Ctrl + C",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl + V",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "흐름",
"Current Color Preview": "현재 색상 미리보기",
"Custom": "관습",
"Data URL": "데이터 URL",
"Data URL:": "데이터 URL :",
"Decrease": "감소",
"Decrease Color Depth": "색상 심도 감소",
"Degree:": "정도:",
"Del": "델",
"Delete": "지우다",
"Delete Selection": "선택 항목 삭제",
"Denoise": "데니스 이스",
"Desaturate Tool": "채도 제거 도구",
"Description:": "기술:",
"Deutsch": "Deutsch",
"Differences": "차이점",
"Differences Down": "차이점",
"Direction:": "방향:",
"Dither": "떨림",
"Dithering:": "디더링 :",
"Dominant color:": "주된 색깔 :",
"Dot Screen": "도트 스크린",
"Down": "하위",
"Duplicate": "복제",
"Duplicate Layer": "중복 레이어",
"Duplicate layer": "레이어 복제",
"Dynamic": "동적",
"Edge": "가장자리",
"Edit": "편집하다",
"Edit text...": "텍스트 수정 ...",
"Effect browser": "효과 브라우저",
"Effects": "효과",
"Effects browser": "효과 브라우저",
"Email:": "이메일:",
"Emboss": "엠보싱",
"Empty selection": "빈 선택",
"Empty selection or type not image.": "이미지를 선택하지 않거나 입력하지 마십시오.",
"Enable autoresize:": "자동 크기 조정 활성화:",
"End": "종료",
"English": "영어",
"English (UK)": "영어(영국)",
"Enrich": "높이다",
"Enter": "시작하다",
"Erase Tool": "지우기 도구",
"Erase on rotate object is disabled. Please rasterize first.": "개체 회전 시 지우기가 비활성화됩니다. 먼저 래스터화해 주세요.",
"Error": "오류",
"Error connecting to service.": "서비스에 연결하는 중 오류가 발생했습니다.",
"Error loading the list of fonts from Google.": "Google에서 글꼴 목록을 로드하는 중에 오류가 발생했습니다.",
"Error registering service worker": "서비스 워커 등록 오류",
"Error: can not find filter:": "오류 : 필터를 찾을 수 없음 :",
"Error: can not find layer with id:": "오류 : ID가있는 레이어를 찾을 수 없습니다.",
"Error: missing details event target": "오류 : 세부 정보 이벤트 대상이 누락되었습니다.",
"Error: unknown layer type:": "오류 : 알 수없는 레이어 유형 :",
"Error: unsupported attribute type:": "오류: 지원되지 않는 속성 유형:",
"Esc": "Esc",
"Escape": "탈출",
"Español": "스페인어",
"Expand edges": "가장자리 확장",
"Exponent:": "멱지수:",
"Export": "내보내다",
"External": "외부",
"Factor:": "인자:",
"File": "파일",
"File name:": "파일 이름:",
"File size:": "파일 크기 :",
"Fill": "가득 따르다",
"Fill Tool": "채우기 도구",
"Fit": "적당한",
"Fit Window": "창에 맞추기",
"Fit window": "창 맞추기",
"Flatten Image": "납작한 이미지",
"Flip": "튀기다",
"FloydSteinberg-serpentine": "FloydSteinberg- 사문석",
"Font": "폰트",
"Français": "Français",
"Full HD, 1080p": "풀 HD, 1080p",
"Full Screen": "전체 화면",
"Full layers data": "전체 레이어 데이터",
"Gap:": "갭:",
"Gaussian Blur": "가우스 흐림",
"Gif delay:": "GIF 지연 :",
"Gingham": "깅엄",
"GitHub:": "GitHub :",
"Gradient Radius:": "기울기 반경 :",
"Grains": "작살",
"Graphics Interchange Format": "그래픽 교환 형식",
"Gray": "회색",
"Grayscale": "그레이 스케일",
"Greek": "그리스 어",
"Green": "녹색",
"Green channel:": "녹색 통로:",
"Greyscale:": "그레이 스케일 :",
"Grid": "그리드",
"Grid on\/off": "그리드 켜기 \/ 끄기",
"Guides": "가이드",
"Guides enabled.": "가이드가 활성화되었습니다.",
"H Radius:": "H 반경 :",
"H. Align:": "H. 정렬 :",
"Heatmap": "히트 맵",
"Height (%):": "높이 (%) :",
"Height:": "신장:",
"Help": "도움",
"Helvetica": "헬 베티 카",
"Hermite": "허 마이트",
"Hex": "마녀",
"Hide": "숨다",
"Histogram": "히스토그램",
"Histogram:": "히스토그램 :",
"Home": "집",
"Horizontal": "수평",
"Horizontal Alignment": "수평 정렬",
"Horizontal blur:": "가로 흐리게 :",
"Horizontal:": "수평의:",
"Hue": "색조",
"Hue Rotate": "색조 회전",
"Hue:": "색조:",
"Image": "영상",
"Image data with multi-layers. Can be opened using miniPaint -": "다중 레이어가있는 이미지 데이터. miniPaint를 사용하여 열 수 있습니다 -",
"Impact": "충격",
"In proportion:": "비례:",
"Increase": "증가하다",
"Information": "정보",
"Inkwell": "잉크 그릇",
"Insert": "끼워 넣다",
"Insert guides": "가이드 삽입",
"Insert new layer": "새 레이어 삽입",
"Instagram Filters": "Instagram 필터",
"Invalid Hex Code": "잘못된 16 진수 코드",
"Italiano": "이탈리아어",
"JPG\/JPEG Format": "JPG \/ JPEG 형식",
"Kerning:": "커닝 :",
"Key-Points": "키 포인트",
"KeyU": "키유",
"Keyboard Shortcuts": "키보드 단축키",
"Keyword:": "예어:",
"Lanczos": "Lanczos",
"Landscape": "풍경",
"Language": "언어",
"Last modified": "최종 수정일",
"Layer": "층",
"Layer details": "레이어 세부 정보",
"Layer is empty.": "레이어가 비어 있습니다.",
"Layer is not compatible with resize": "레이어는 크기 조정과 호환되지 않습니다.",
"Layer is vector, convert it to raster to apply this tool.": "레이어는 벡터이므로 래스터로 변환하여이 도구를 적용합니다.",
"Layers": "레이어",
"Layers:": "레이어 :",
"Layout:": "공들여 나열한 것:",
"Left": "왼쪽",
"Left to Right": "좌에서 우로",
"Level:": "수평:",
"Levels:": "레벨 :",
"Lietuvių": "Lietuvių",
"Lo-fi": "Lo-Fi",
"Luminance:": "휘도 :",
"Luminosity": "밝기",
"Magic Eraser Tool": "매직 지우개 도구",
"Merge Down": "병합",
"Merge Layers": "계층을 병합하다",
"Merged": "병합 됨",
"Metrics": "지표",
"Middle": "가운데",
"Missing at least 1 size parameter.": "크기 매개 변수가 1 개 이상 누락되었습니다.",
"Missing permissions to write to Clipboard.cc": "Clipboard.cc에 쓸 수있는 권한이 없습니다.",
"Mode:": "방법:",
"Module function not found.": "모듈 기능을 찾을 수 없습니다.",
"Modules class not found:": "모듈 클래스를 찾을 수 없음 :",
"Monospace": "고정 폭",
"Mosaic": "모자이크",
"Mouse:": "쥐:",
"Move": "움직임",
"Move Layer": "레이어 이동",
"Move layer down": "레이어를 아래로 이동",
"Move layer up": "레이어를 위로 이동",
"Name:": "이름:",
"Negative": "부정",
"New": "새로운",
"New Bezier Layer": "새로운 베지어 레이어",
"New Brush Layer": "새 브러시 레이어",
"New Ellipse Layer": "새 타원 레이어",
"New File": "새로운 파일",
"New Gradient Layer": "새로운 그라디언트 레이어",
"New Layer": "새 레이어",
"New Line Layer": "새 라인 레이어",
"New Pencil Layer": "새 연필 레이어",
"New Polygon Layer": "새로운 폴리곤 레이어",
"New Rectangle Layer": "새로운 직사각형 레이어",
"New Text Layer": "새 텍스트 레이어",
"New file": "새로운 파일",
"New from Selection": "선택 항목의 새로운 기능",
"New layer": "새 레이어",
"Next": "다음",
"Night Vision": "나이트 비전",
"None": "없음",
"Nothing is selected.": "아무것도 선택되지 않았습니다.",
"Offset X:": "오프셋 X :",
"Offset Y:": "오프셋 Y :",
"Oil": "기름",
"Ok": "승인",
"Online image editor.": "온라인 이미지 편집기.",
"Opacity": "불투명",
"Opacity:": "불투명:",
"Open": "열다",
"Open Data URL": "공개 데이터 URL",
"Open Directory": "오픈 디렉토리",
"Open File": "파일 열기",
"Open File Data URL": "파일 데이터 URL 열기",
"Open File URL": "파일 URL 열기",
"Open File Webcam": "파일 열기 웹캠",
"Open Image": "이미지 열기",
"Open JSON File": "JSON 파일 열기",
"Open Test Template": "테스트 템플릿 열기",
"Open URL": "URL 열기",
"Open data URL": "공개 데이터 URL",
"Open from Webcam": "웹캠에서 열기",
"Original Size": "원본 크기",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG-이미지를 SVG로 변환",
"PageDown": "PageDown",
"PageUp": "페이지 위로",
"Palette": "팔레트",
"Parameter #1:": "매개 변수 # 1 :",
"Parameter #2:": "매개 변수 # 2 :",
"Paste": "풀",
"Pencil": "연필",
"Percentage:": "백분율:",
"Pixels:": "픽셀 :",
"Placeholder comment for color channels": "색상 채널에 대한 자리 표시 자 주석",
"Placeholder comment for color picker": "색상 선택기에 대한 자리 표시 자 주석",
"Placeholder comment for color swatches": "색상 견본에 대한 자리 표시 자 주석",
"Portable Network Graphics": "휴대용 네트워크 그래픽",
"Portrait": "초상화",
"Português": "Português",
"Position:": "위치:",
"Power:": "힘:",
"Preview": "시사",
"Previous": "너무 이른",
"Previous layer must be image, convert it to raster to apply this tool.": "이전 레이어는 이미지 여야하며이 도구를 적용하려면 래스터로 변환해야합니다.",
"Print": "인쇄",
"Quality:": "품질:",
"Quick Load": "빠른로드",
"Quick Save": "빠른 저장",
"REMOVE.BG - Remove Image Background": "REMOVE.BG-이미지 배경 제거",
"Radial": "방사형",
"Radial gradient": "방사형 그래디언트",
"Radius:": "반지름:",
"Range:": "범위:",
"Red": "빨간",
"Red channel:": "적색 통로:",
"Redo": "다시 하다",
"Remove all": "모두 제거",
"Rename": "이름 바꾸기",
"Rename Layer": "레이어 이름 변경",
"Rendered with errors.": "오류와 함께 렌더링됩니다.",
"Rendering...": "표현...",
"Replace Color": "색상 바꾸기",
"Replace color": "색상 바꾸기",
"Replacement:": "바꿔 놓음:",
"Report Issues": "문제 신고",
"Reset": "다시 놓기",
"Resize": "크기 조정",
"Resize Boundary": "경계 크기 조정",
"Resize Layer": "레이어 크기 조정",
"Resize Layers": "레이어 크기 조정",
"Resize Text Layer": "텍스트 레이어 크기 조정",
"Resized as background": "배경으로 크기 조정",
"Resized:": "크기 조정됨:",
"Resolution:": "해결:",
"Restore Alpha": "알파 복원",
"Right": "권리",
"Right angle:": "직각:",
"Right to Left": "오른쪽에서 왼쪽으로",
"Rotate": "회전",
"Rotate Layer": "레이어 회전",
"Rotate is not supported on this type of object. Convert to raster?": "회전은이 유형의 객체에서 지원되지 않습니다. 래스터로 변환 하시겠습니까?",
"Rotate left": "왼쪽으로 회전",
"Rotate:": "회전 :",
"Ruler": "자",
"SQUOOSH - Compress and Compare Images": "SQUOOSH-이미지 압축 및 비교",
"Saturate": "가득한",
"Saturation": "포화",
"Saturation:": "포화:",
"Save As": "다른 이름으로 저장",
"Save As Data URL": "데이터 URL로 저장",
"Save as": "다른 이름으로 저장",
"Save as type:": "유형으로 저장 :",
"Save layers:": "레이어 저장 :",
"Scaling up is not supported in Hermite, using Lanczos.": "Lanczos를 사용하는 Hermite에서는 확장이 지원되지 않습니다.",
"Scroll down": "아래로 스크롤",
"Scroll up": "스크롤",
"Search": "수색",
"Search Images": "이미지 검색",
"Search for Font": "글꼴 검색",
"Search:": "찾다:",
"Select All": "모두 선택",
"Select Text Layer": "텍스트 레이어 선택",
"Select object tool": "오브젝트 도구 선택",
"Selected": "선택된",
"Selection Tool": "선택 도구",
"Sensitivity:": "감광도:",
"Separated": "분리됨",
"Separated (original types)": "분리형(원본 유형)",
"Sepia": "세피아",
"Set Image Size": "이미지 크기 설정",
"Settings": "설정",
"Shadow": "그림자",
"Shapes": "모양",
"Shapes (H)": "모양(H)",
"Sharpen": "갈다",
"Sharpen Tool": "선명 도구",
"Sharpen:": "갈다:",
"Shift + S": "쉬프트 + S",
"Shortcut Key:": "바로 가기 키:",
"Show": "보여주다",
"Show \/ Hide": "표시 \/ 숨기기",
"Show file size:": "파일 크기 표시 :",
"Simple": "단순한",
"Size is too big, max": "크기가 너무 큽니다.",
"Size:": "크기:",
"Skip - layer must be image.": "건너 뛰기 - 레이어가 이미지 여야합니다.",
"Solarize": "솔라 이즈",
"Sorry, cold not load getUserMedia() data:": "죄송합니다. getUserMedia () 데이터를로드하지 마세요.",
"Sorry, image could not be loaded.": "죄송합니다. 이미지를로드 할 수 없습니다.",
"Sorry, image could not be loaded. Try copy image and paste it.": "죄송합니다. 이미지를로드 할 수 없습니다. 이미지 복사 및 붙여 넣기를 시도하십시오.",
"Sorry, image is too big, max 5 MB.": "죄송합니다. 이미지가 너무 크고 최대 5MB입니다.",
"Source coordinates saved.": "소스 좌표가 저장되었습니다.",
"Source is empty, right click on image or use long press to save source position.": "소스가 비어 있습니다. 이미지를 마우스 오른쪽 버튼으로 클릭하거나 길게 눌러 소스 위치를 저장하세요.",
"Sprites": "스프라이트",
"Square": "광장",
"Stream:": "흐름:",
"Strength:": "힘:",
"Strict": "엄격한",
"TINYPNG - Compress PNG and JPEG": "TINYPNG-PNG 및 JPEG 압축",
"Tab": "탭",
"Tag Image File Format": "태그 이미지 파일 형식",
"Tahoma": "타호 마",
"Target:": "목표:",
"The quick brown fox jumps over the lazy dog.": "날렵한 갈색여우가 게으른 개를 뛰어넘습니다.",
"There": "그곳에",
"There are no layers behind.": "뒤에 레이어가 없습니다.",
"There is only 1 layer.": "단 하나의 레이어가 있습니다.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "이 레이어에는 이미지가 있어야합니다. 이 도구를 적용하려면 래스터로 변환하십시오.",
"Tilt Shift": "경사 변화",
"Times New Roman": "Times New Roman",
"Toaster": "토스터에",
"Toggle": "비녀장",
"Toggle Color Channels": "색상 채널 전환",
"Toggle Color Picker": "색상 선택기 전환",
"Toggle Menu": "토글 메뉴",
"Toggle Swatches": "견본 전환",
"Tools": "도구들",
"Top": "상단",
"Top to Bottom": "위에서 아래로",
"Total pixels:": "총 픽셀 수 :",
"Translate": "옮기다",
"Translate Layer": "레이어 번역",
"Translate error, can not find dictionary:": "번역 오류, 사전을 찾을 수 없음 :",
"Transparent:": "투명한:",
"Trim": "손질",
"Trim Layers": "레이어 트림",
"Trim borders:": "테두리 자르기 :",
"Trim layer:": "레이어 다듬기 :",
"Trim white color?": "흰색을 다듬을까요?",
"Type:": "유형:",
"Türkçe": "Türkçe",
"Undo": "끄르다",
"Unique colors:": "독특한 색상 :",
"Up": "쪽으로",
"Update": "업데이트",
"Update Brush Layer": "브러시 레이어 업데이트",
"Update Pencil Layer": "연필 레이어 업데이트",
"Update guides": "가이드 업데이트",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Ctrl + V 키보드 단축키를 사용하여 클립 보드에서 붙여 넣기하십시오.",
"V Radius:": "V 반경 :",
"V. Align:": "V. 정렬 :",
"Valencia": "발렌시아",
"Verdana": "Verdana",
"Version:": "번역:",
"Vertical": "수직선",
"Vertical Alignment": "수직 정렬",
"Vertical blur:": "수직 흐림 효과 :",
"Vertical:": "수직의:",
"Vibrance": "활기찬",
"View": "보다",
"Vignette": "삽화",
"ViliusL": "ViliusL",
"Vintage": "포도 수확",
"Webcam": "웹캠",
"Webcam #": "웹캠 #",
"Website:": "웹 사이트 :",
"Weppy File Format": "Weppy 파일 형식",
"Width (%):": "너비 (%) :",
"Width:": "폭:",
"Windows Bitmap": "Windows 비트 맵",
"Word": "워드",
"Word + Letter": "단어 + 문자",
"Wrap At:": "줄 바꿈 :",
"Wrap:": "싸다:",
"Wrong dimensions": "잘못된 치수",
"Wrong file type, must be image or json.": "잘못된 파일 유형. 이미지 또는 json이어야합니다.",
"X end:": "X 끝 :",
"X position:": "X 위치 :",
"X start:": "X 시작 :",
"X-Pro II": "X-Pro II",
"Y end:": "Y 끝 :",
"Y position:": "Y 위치 :",
"Y start:": "Y 시작 :",
"You can also drag and drop items into browser.": "항목을 브라우저로 끌어다 놓을 수도 있습니다.",
"Your browser does not support canvas or JavaScript is not enabled.": "브라우저가 캔버스를 지원하지 않거나 JavaScript가 활성화되어 있지 않습니다.",
"Your browser does not support this format.": "브라우저가이 형식을 지원하지 않습니다.",
"Your search did not match any images.": "검색어와 일치하는 이미지가 없습니다.",
"Zoom": "줌",
"Zoom Blur": "줌 블러",
"Zoom In": "확대",
"Zoom Out": "축소",
"Zoom blur": "줌 흐림 효과",
"Zoom in": "확대",
"Zoom out": "축소",
"Zoom:": "줌:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Pašalinant anuliavimo istoriją įvyko problema. Tai",
"About": "Apie",
"Active": "Aktyvus",
"Aden": "Aden",
"Advanced": "Pažangus",
"All": "Visi",
"Alpha": "Alfa",
"Alpha:": "Alfa:",
"Anonymous": "Anoniminis",
"Anti aliasing": "Sulieti",
"Application markup may have changed,": "Programos žymėjimas galėjo pasikeisti,",
"Arial": "Arial",
"Arrow": "Rodyklė",
"ArrowDown": "Rodyklė žemyn",
"ArrowLeft": "Rodyklė kairėn",
"ArrowRight": "RodyklėDešinė",
"ArrowUp": "„ArrowUp“",
"Author:": "Autorius:",
"Auto Adjust Colors": "Sureguliuoti spalvas",
"Auto Kerning": "„Auto Kerning“",
"Average:": "Vidurkis:",
"Backspace": "Backspace",
"Base": "Bazė",
"Basic": "Paprastas",
"Black and White": "Juoda ir balta",
"Blue": "Mėlynas",
"Blue channel:": "Mėlyna kanalas:",
"Blueprint": "Techninis piešinys",
"Blur Radius:": "Migla spindulys:",
"Blur Tool": "Neryškus įrankis",
"Blur power:": "Blur stiprumas:",
"Borders": "Ribojasi",
"Bottom": "Apačia",
"Bottom to Top": "Iš apačios į viršų",
"Bounds:": "Ribos:",
"Box": "Dėžė",
"Box Blur": "Box Blur",
"Box blur": "Langelis blur",
"Brightness": "Ryškumas",
"Brightness:": "Ryškumas:",
"Bulge\/Pinch Tool": "Išsipūtimo \/ prispaudimo įrankis",
"Burn": "Deginti",
"Can not animate 1 layer.": "Negalima animuoti 1 sluoksniu.",
"Can not find previous layer.": "Negaliu rasti ankstesnio sluoksnio.",
"Can not use this tool on current layer: image already takes all area.": "Negalima naudoti šio įrankio dabartiniame sluoksnyje: vaizdas jau užima visą plotą.",
"Cancel": "Atšaukti",
"Canvas Size": "Paveikslo Dydis",
"Center": "Centras",
"Center x:": "Centras x:",
"Center y:": "Centras y:",
"Center:": "Centras:",
"Change Composition": "Keisti kompoziciją",
"Change Layer Details": "Keisti išsamią informaciją",
"Change Opacity": "Pakeiskite neskaidrumą",
"Channel:": "Kanalas:",
"Circle": "Ratas",
"Clarendon": "Klarendonas",
"Clear": "Aiškus",
"Clear Selection": "Išvalyti pasirinkimą",
"Clone Tool": "Klonų įrankis",
"Clone count:": "Klonų skaičius:",
"Clone tool disabled for resized image. Please rasterize first.": "Klonavimo įrankis išjungtas norint pakeisti vaizdo dydį. Pirmiausia rastruokite.",
"Cloned edges": "Klonuoti kraštai",
"Close": "Uždaryti",
"Color #": "Spalva #",
"Color Corrections": "Spalvų korekcijos",
"Color Palette": "Spalvų paletė",
"Color Zoom": "Spalvų mastelio keitimas",
"Color alpha value can not be zero.": "Spalvų alfa vertė negali būti lygi nuliui.",
"Color to Alpha": "Spalva alfa",
"Color zoom": "Spalvų priartinimas",
"Color:": "Spalva:",
"Colors": "Spalvos",
"Colors:": "Spalvos:",
"Common Filters": "Bendri filtrai",
"Composition": "Kompozicija",
"Composition:": "Sudėtis:",
"Content Fill": "Turinio užpildymas",
"Contrast": "Kontrastas",
"Contrast:": "Kontrastas:",
"Convert layer to raster": "Konvertuoti sluoksnį į rastrinį",
"Convert to Raster": "Konvertuoti į rastrą",
"Copy Selection": "Kopijuoti pasirinkimą",
"Copy to Clipboard": "Nukopijuoti į iškarpinę",
"Courier": "Courier",
"Crop Tool": "Apkarpymo įrankis",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Apkarpyti pasuktą sluoksnį negalima. Konvertuokite jį į rastrą, kad galėtumėte tęsti.",
"Ctrl + C": "Ctrl + C",
"Ctrl+A": "Ctrl+A",
"Ctrl+C": "Ctrl+C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl+V",
"Ctrl+Y": "Ctrl+Y",
"Ctrl+Z": "Ctrl+Z",
"Current": "Dabartinis",
"Current Color Preview": "Dabartinė spalvų peržiūra",
"Custom": "Kitas",
"Data URL": "Duomenų adresas",
"Data URL:": "Duomenų adresas:",
"Decrease": "Mažinti",
"Decrease Color Depth": "Sumažinti spalvų gylį",
"Degree:": "Laipsnis:",
"Del": "Del",
"Delete": "Ištrinti",
"Delete Selection": "Ištrinti pasirinkimą",
"Denoise": "Sumažinti triukšmą",
"Desaturate Tool": "Desaturato įrankis",
"Description:": "Aprašymas:",
"Deutsch": "Deutsch",
"Differences": "Skirtumai",
"Differences Down": "Skirtumai žemyn",
"Direction:": "Kryptis:",
"Dither": "Papildymas",
"Dithering:": "Papildymu:",
"Dominant color:": "Dominuojanti spalva:",
"Dot Screen": "Taškų ekranas",
"Down": "Žemyn",
"Duplicate": "Pasikartojantis",
"Duplicate Layer": "Pasikartojantis sluoksnis",
"Duplicate layer": "Dubliuoti sluoksnį",
"Dynamic": "Dinamiškas",
"Edge": "Kraštas",
"Edit": "Redaguoti",
"Edit text...": "Redaguoti tekstą ...",
"Effect browser": "Poveikio naršyklė",
"Effects": "Efektai",
"Effects browser": "Efektų naršyklė",
"Email:": "El. paštas:",
"Emboss": "Įspausti",
"Empty selection": "Tuščias pasirinkimas",
"Empty selection or type not image.": "Tuščias pasirinkimas arba įveskite ne vaizdą.",
"Enable autoresize:": "Įjungti automatinį dydžio nustatymą:",
"End": "Galas",
"English": "Anglų",
"English (UK)": "anglų (JK)",
"Enrich": "Praturtinti",
"Enter": "Įveskite",
"Erase Tool": "Ištrinti įrankį",
"Erase on rotate object is disabled. Please rasterize first.": "Ištrynimas sukant objektą išjungtas. Pirmiausia rastruokite.",
"Error": "Klaida",
"Error connecting to service.": "Klaida prisijungiant prie paslaugos.",
"Error loading the list of fonts from Google.": "Įkeliant šriftų sąrašą iš „Google“ įvyko klaida.",
"Error registering service worker": "Klaida registruojant aptarnavimo darbuotoją",
"Error: can not find filter:": "Klaida: nepavyksta rasti filtro:",
"Error: can not find layer with id:": "Klaida: nepavyksta rasti sluoksnio su ID:",
"Error: missing details event target": "Klaida: trūksta detalių įvykio tikslo",
"Error: unknown layer type:": "Klaida: nežinomas sluoksnio tipas:",
"Error: unsupported attribute type:": "Klaida: nepalaikomas atributo tipas:",
"Esc": "Esc",
"Escape": "Pabegti",
"Español": "Español",
"Expand edges": "Išskleiskite kraštus",
"Exponent:": "Eksponentė:",
"Export": "Eksportuoti",
"External": "Išorinis",
"Factor:": "Veiksnys:",
"File": "Failas",
"File name:": "Failo pavadinimas:",
"File size:": "Failo dydis:",
"Fill": "Pildyti",
"Fill Tool": "Užpildymo įrankis",
"Fit": "Talpinti",
"Fit Window": "Tinkamas langas",
"Fit window": "Pritaikyti langą",
"Flatten Image": "Išlyginti vaizdą",
"Flip": "Apversti",
"FloydSteinberg-serpentine": "Floydsteinberg-serpentinas",
"Font": "Šriftas",
"Français": "Français",
"Full HD, 1080p": "Full HD, 1080p",
"Full Screen": "Per visą ekraną",
"Full layers data": "Visų sluoksnių duomenys",
"Gap:": "Atotrūkis:",
"Gaussian Blur": "Gauso suliejimo",
"Gif delay:": "Gif delsimas:",
"Gingham": "Gingamas",
"GitHub:": "Github:",
"Gradient Radius:": "Gradientas spindulys:",
"Grains": "Grūdėtumas",
"Graphics Interchange Format": "Grafikos mainų formatas",
"Gray": "Pilkas",
"Grayscale": "Pelės skalė",
"Greek": "graikų",
"Green": "Žalias",
"Green channel:": "Žalias kanalas:",
"Greyscale:": "Pilkieji pustoniai:",
"Grid": "Tinklelis",
"Grid on\/off": "Tinklelis",
"Guides": "Vadovai",
"Guides enabled.": "Vadovai įjungti.",
"H Radius:": "H spindulys:",
"H. Align:": "H. Lygiuoti:",
"Heatmap": "Spalvinė diagrama",
"Height (%):": "Aukštis (%):",
"Height:": "Aukštis:",
"Help": "Pagalba",
"Helvetica": "Helvetica",
"Hermite": "Hermite",
"Hex": "Hex",
"Hide": "Slėpti",
"Histogram": "Histograma",
"Histogram:": "Histograma:",
"Home": "Namai",
"Horizontal": "Horizontali",
"Horizontal Alignment": "Horizontalus išlyginimas",
"Horizontal blur:": "Horizontalus miglotas vaizdas:",
"Horizontal:": "Horizontalus:",
"Hue": "Atspalvis",
"Hue Rotate": "Atspalvis pasukti",
"Hue:": "Atspalvis:",
"Image": "Vaizdas",
"Image data with multi-layers. Can be opened using miniPaint -": "Vaizdo duomenys su kelių sluoksnių. gali būti atidarytas naudojant minipaint -",
"Impact": "Poveikis",
"In proportion:": "Proporcingai:",
"Increase": "Padidinti",
"Information": "Informacija",
"Inkwell": "Rašalo kasykla",
"Insert": "Įdėti",
"Insert guides": "Įdėkite vadovus",
"Insert new layer": "Įdėkite naują sluoksnį",
"Instagram Filters": "„Instagram“ filtrai",
"Invalid Hex Code": "Netinkamas šešiakampis kodas",
"Italiano": "Italų kalba",
"JPG\/JPEG Format": "JPG \/ JPEG formatas",
"Kerning:": "Kerningas:",
"Key-Points": "Pagrindiniai klausimai",
"KeyU": "KeyU",
"Keyboard Shortcuts": "Klaviatūros nuorodos",
"Keyword:": "Raktinis žodis:",
"Lanczos": "Lanczos",
"Landscape": "Peizažas",
"Language": "Kalba",
"Last modified": "Paskutinį kartą keistas",
"Layer": "Sluoksnis",
"Layer details": "Sluoksnio detalės",
"Layer is empty.": "Sluoksnis tuščias.",
"Layer is not compatible with resize": "Sluoksnis nesuderinamas su dydžio keitimu",
"Layer is vector, convert it to raster to apply this tool.": "Sluoksnis yra vektorius, konvertuokite jį į rastrą, kad pritaikytumėte šį įrankį.",
"Layers": "Sluoksniai",
"Layers:": "Sluoksniai:",
"Layout:": "Išdėstymas:",
"Left": "Kairėje",
"Left to Right": "Iš kairės į dešinę",
"Level:": "Lygis:",
"Levels:": "Lygiais:",
"Lietuvių": "Lietuvių",
"Lo-fi": "Lo-fi",
"Luminance:": "Skaisčio:",
"Luminosity": "Šviesumas",
"Magic Eraser Tool": "„Magic Eraser“ įrankis",
"Merge Down": "Sujungti žemyn",
"Merge Layers": "Sujungti sluoksnius",
"Merged": "Sujungta",
"Metrics": "Metrika",
"Middle": "Vidurinis",
"Missing at least 1 size parameter.": "Trūksta bent 1 dydžio parametro.",
"Missing permissions to write to Clipboard.cc": "Trūksta leidimų rašyti į „Clipboard.cc“",
"Mode:": "Režimas:",
"Module function not found.": "Modulio funkcija nerasta.",
"Modules class not found:": "Modulio klasė nerasta:",
"Monospace": "Monospace",
"Mosaic": "Mozaika",
"Mouse:": "Pelė:",
"Move": "Perkelti",
"Move Layer": "Perkelti sluoksnį",
"Move layer down": "Perkelkite sluoksnį žemyn",
"Move layer up": "Perkelti sluoksnį aukštyn",
"Name:": "Vardas:",
"Negative": "Neigiamas",
"New": "Naujas",
"New Bezier Layer": "Naujas Bezier sluoksnis",
"New Brush Layer": "Naujas teptuko sluoksnis",
"New Ellipse Layer": "Naujas elipsės sluoksnis",
"New File": "Naujas failas",
"New Gradient Layer": "Naujas gradiento sluoksnis",
"New Layer": "Naujas sluoksnis",
"New Line Layer": "Naujas eilutės sluoksnis",
"New Pencil Layer": "Naujas pieštukų sluoksnis",
"New Polygon Layer": "Naujas daugiakampio sluoksnis",
"New Rectangle Layer": "Naujas stačiakampio sluoksnis",
"New Text Layer": "Naujas teksto sluoksnis",
"New file": "Naujas failas",
"New from Selection": "Nauja iš pasirinkimo",
"New layer": "Nauja sluoksnis",
"Next": "Kitas",
"Night Vision": "Naktinis matymas",
"None": "Nė vienas",
"Nothing is selected.": "Niekas nėra pasirinktas.",
"Offset X:": "Nuokrypis x:",
"Offset Y:": "Kompensuoti:",
"Oil": "Aliejus",
"Ok": "Gerai",
"Online image editor.": "Internetinis vaizdo redaktorius.",
"Opacity": "Nepermatomumas",
"Opacity:": "Nepermatomumas:",
"Open": "Atidaryti",
"Open Data URL": "Atidaryti duomenų URL",
"Open Directory": "Atidaryti katalogą",
"Open File": "Atidaryti failą",
"Open File Data URL": "Atidarykite failo duomenų URL",
"Open File URL": "Atidarykite failo URL",
"Open File Webcam": "Atidarykite „File Webcam“",
"Open Image": "Atidarykite vaizdą",
"Open JSON File": "Atidarykite JSON failą",
"Open Test Template": "Atidarykite testavimo šabloną",
"Open URL": "Atidaryti url",
"Open data URL": "Atidaryti duomenų url",
"Open from Webcam": "Atidarykite iš interneto kameros",
"Original Size": "Originalus dydis",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - konvertuoti vaizdą į SVG",
"PageDown": "„PageDown“",
"PageUp": "Į viršų",
"Palette": "Paletė",
"Parameter #1:": "Parametras Nr. 1:",
"Parameter #2:": "Parametras # 2:",
"Paste": "Įkelti",
"Pencil": "Pieštukas",
"Percentage:": "Procentas:",
"Pixels:": "Taškai:",
"Placeholder comment for color channels": "Spalvotų kanalų vietos rezervatorius",
"Placeholder comment for color picker": "Spalvų parinkiklio vietos komentaras",
"Placeholder comment for color swatches": "Spalvų pavyzdžių vietos komentaras",
"Portable Network Graphics": "Nešiojama tinklo grafika",
"Portrait": "Portretas",
"Português": "Português",
"Position:": "Padėtis:",
"Power:": "Galia:",
"Preview": "Peržiūrėti",
"Previous": "Ankstesnis",
"Previous layer must be image, convert it to raster to apply this tool.": "Ankstesnis sluoksnis turi būti vaizdas, konvertuoti jį į rastrą, kad būtų taikomas šis įrankis.",
"Print": "Spausdinti",
"Quality:": "Kokybė:",
"Quick Load": "Greitas įkrovimas",
"Quick Save": "Greitas išsaugojimas",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Pašalinti vaizdo foną",
"Radial": "Radialinis",
"Radial gradient": "Radialinis gradientas",
"Radius:": "Spindulys:",
"Range:": "Kategorijos:",
"Red": "Raudonas",
"Red channel:": "Raudonasis kanalas:",
"Redo": "Perdaryti",
"Remove all": "Pašalinti visus",
"Rename": "Pervadinti",
"Rename Layer": "Pervardyti sluoksnį",
"Rendered with errors.": "Pateikta su klaidomis.",
"Rendering...": "Perduodama ...",
"Replace Color": "Pakeiskite spalvą",
"Replace color": "Pakeiskite spalvą",
"Replacement:": "Pakeitimas:",
"Report Issues": "Pranešti apie problemas",
"Reset": "Atstatyti",
"Resize": "Keisti dydį",
"Resize Boundary": "Keisti ribos dydį",
"Resize Layer": "Keisti sluoksnio dydį",
"Resize Layers": "Keisti sluoksnių dydį",
"Resize Text Layer": "Keisti teksto sluoksnio dydį",
"Resized as background": "Pakeista kaip fonas",
"Resized:": "Pakeistas dydis:",
"Resolution:": "Rezoliucija:",
"Restore Alpha": "Atkurti alfa",
"Right": "Teisingai",
"Right angle:": "Dešinysis kampas:",
"Right to Left": "Iš dešinės į kairę",
"Rotate": "Sukti",
"Rotate Layer": "Pasukti sluoksnį",
"Rotate is not supported on this type of object. Convert to raster?": "Šio tipo objektuose nepavyksta pakeisti rotacijos. konvertuoti į rastrą?",
"Rotate left": "Pasukti į kairę",
"Rotate:": "Pasukti:",
"Ruler": "Valdovas",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - suspauskite ir palyginkite vaizdus",
"Saturate": "Saturate",
"Saturation": "Sodrumas",
"Saturation:": "Spalvingumas:",
"Save As": "Išsaugoti kaip",
"Save As Data URL": "Išsaugoti kaip duomenų URL",
"Save as": "Išsaugoti kaip",
"Save as type:": "Išsaugoti kaip:",
"Save layers:": "Išsaugoti sluoksnius:",
"Scaling up is not supported in Hermite, using Lanczos.": "„Hermite“, naudojant „Lanczos“, mastelio didinimas nepalaikomas.",
"Scroll down": "Slinkti žemyn",
"Scroll up": "Slinkite aukštyn",
"Search": "Paieška",
"Search Images": "Ieškoti vaizdų",
"Search for Font": "Ieškoti šrifto",
"Search:": "Paieška:",
"Select All": "Pasirinkti viską",
"Select Text Layer": "Pasirinkite Teksto sluoksnis",
"Select object tool": "Pasirinkite objektas įrankis",
"Selected": "Pasirinkti",
"Selection Tool": "Pasirinkimo įrankis",
"Sensitivity:": "Jautrumas:",
"Separated": "Atskirtas",
"Separated (original types)": "Atskirti (originali tipai)",
"Sepia": "Sepia",
"Set Image Size": "Nustatykite vaizdo dydį",
"Settings": "Nustatymai",
"Shadow": "Šešėlis",
"Shapes": "Formos",
"Shapes (H)": "Formos (H)",
"Sharpen": "Pagaląsti",
"Sharpen Tool": "Aštrinimo įrankis",
"Sharpen:": "Paryškinti:",
"Shift + S": "Shift + S",
"Shortcut Key:": "Spartusis klavišas:",
"Show": "Rodyti",
"Show \/ Hide": "Rodyti \/ Slėpti",
"Show file size:": "Rodyti failo dydį:",
"Simple": "Paprastas",
"Size is too big, max": "Dydis yra per didelis, maks",
"Size:": "Dydis:",
"Skip - layer must be image.": "Praleisti - sluoksnis turi būti vaizdas.",
"Solarize": "Soliarizacija",
"Sorry, cold not load getUserMedia() data:": "Deja, šalta, neįkelkite „getUserMedia“ () duomenų:",
"Sorry, image could not be loaded.": "Deja, nepavyko įkelti vaizdo.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Deja, vaizdas negali būti įkeltas. pabandykite kopijuoti nuotrauką ir įklijuoti ją.",
"Sorry, image is too big, max 5 MB.": "Atsiprašome, vaizdas yra per didelis, daugiausiai 5 MB.",
"Source coordinates saved.": "Šaltinio koordinatės išsaugotos.",
"Source is empty, right click on image or use long press to save source position.": "Šaltinis tuščias, dešiniuoju pelės mygtuku spustelėkite vaizdą arba naudokite ilgą paspaudimą, kad išsaugotumėte šaltinio padėtį.",
"Sprites": "Sprites",
"Square": "Langelis",
"Stream:": "Srautas:",
"Strength:": "Jėga:",
"Strict": "Griežtas",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - suspausti PNG ir JPEG",
"Tab": "Tab",
"Tag Image File Format": "Žymės vaizdo failo formatas",
"Tahoma": "Tahoma",
"Target:": "Tikslas:",
"The quick brown fox jumps over the lazy dog.": "Greita rudoji lapė peršoka per tinginį šunį.",
"There": "Ten",
"There are no layers behind.": "Už sluoksnių nėra.",
"There is only 1 layer.": "Yra tik 1 sluoksnis.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Sluoksnis turi būti paveiksliukas, konvertuokite jį į rastrą, kad pritaikyti šį įrankį.",
"Tilt Shift": "Tento perkelimas",
"Times New Roman": "Times New Roman",
"Toaster": "Skrudintuvas",
"Toggle": "Perjungti",
"Toggle Color Channels": "Perjungti spalvų kanalus",
"Toggle Color Picker": "Perjungti spalvų rinkiklį",
"Toggle Menu": "Perjungti meniu",
"Toggle Swatches": "Perjungti pavyzdžius",
"Tools": "Įrankiai",
"Top": "Į viršų",
"Top to Bottom": "Nuo viršaus iki apačios",
"Total pixels:": "Iš viso taškų:",
"Translate": "Versti",
"Translate Layer": "Versti sluoksnį",
"Translate error, can not find dictionary:": "Versti klaidą, negali rasti žodyną:",
"Transparent:": "Skaidri:",
"Trim": "Apkarpyti",
"Trim Layers": "Apdailos sluoksniai",
"Trim borders:": "Apkirpti kraštus:",
"Trim layer:": "Trim sluoksnis:",
"Trim white color?": "Trim balta spalva?",
"Type:": "Tipas:",
"Türkçe": "Türkçe",
"Undo": "Anuliuoti",
"Unique colors:": "Unikalios spalvos:",
"Up": "Aukštyn",
"Update": "Atnaujinti",
"Update Brush Layer": "Atnaujinti teptuko sluoksnį",
"Update Pencil Layer": "Atnaujinkite pieštukų sluoksnį",
"Update guides": "Atnaujinti vadovus",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Naudokite \"ctrl + v\" spartieji klavišai, kuriuos norite įklijuoti iš iškarpinės.",
"V Radius:": "V spindulys:",
"V. Align:": "V. Sulyginti:",
"Valencia": "Valensija",
"Verdana": "Verdana",
"Version:": "Versija:",
"Vertical": "Vertikalus",
"Vertical Alignment": "Vertikalus išlyginimas",
"Vertical blur:": "Vertikalus plyšimas:",
"Vertical:": "Vertikalus:",
"Vibrance": "Rezonansas",
"View": "Žiūrėti",
"Vignette": "Vinjetė",
"ViliusL": "Viliusl",
"Vintage": "Senoviškas",
"Webcam": "Internetinė kamera",
"Webcam #": "Internetinė kamera #",
"Website:": "Interneto svetainė:",
"Weppy File Format": "Weppy failo formatas",
"Width (%):": "Plotis (%):",
"Width:": "Plotis:",
"Windows Bitmap": "„Windows Bitmap“",
"Word": "Žodis",
"Word + Letter": "Žodis + laiškas",
"Wrap At:": "Apvyniokite:",
"Wrap:": "Apvyniojimas:",
"Wrong dimensions": "Neteisingi matmenys",
"Wrong file type, must be image or json.": "Neteisingas failo tipas, turi būti paveikslėlis arba json.",
"X end:": "X pabaiga:",
"X position:": "X pozicija:",
"X start:": "X pradžia::",
"X-Pro II": "„X-Pro II“",
"Y end:": "Y pabaiga:",
"Y position:": "Y pozicija:",
"Y start:": "Y pradžia:",
"You can also drag and drop items into browser.": "Taip pat galite vilkti elementus į naršyklę.",
"Your browser does not support canvas or JavaScript is not enabled.": "Jūsų naršyklė nepalaiko drobės ar javascript nėra įjungtas.",
"Your browser does not support this format.": "Jūsų naršyklė nepalaiko šio formato.",
"Your search did not match any images.": "Jūsų paieška neatitiko jokių vaizdų.",
"Zoom": "Zoom",
"Zoom Blur": "Zoom Blur",
"Zoom In": "Priartinti",
"Zoom Out": "Nutolinti",
"Zoom blur": "Padidinti blur",
"Zoom in": "Priartinti",
"Zoom out": "Nutolinti",
"Zoom:": "Priartinimas:"
}
+514
View File
@@ -0,0 +1,514 @@
{
"A problem occurred while removing undo history. It": "Er is een probleem opgetreden bij het verwijderen van de ongedaanmaakgeschiedenis. Het",
"About": "Over",
"Active": "Actief",
"Aden": "Aden",
"Advanced": "Geavanceerd",
"All": "Alle",
"Alpha": "Alpha",
"Alpha:": "Alpha:",
"Anonymous": "Anoniem",
"Anti aliasing": "Anti-aliasing",
"Application markup may have changed,": "De opmaak van de applicatie is mogelijk gewijzigd,",
"Arial": "Arial",
"Arrow": "Pijl",
"ArrowDown": "ArrowDown",
"ArrowLeft": "Pijl naar links",
"ArrowRight": "Pijl naar rechts",
"ArrowUp": "Pijl omhoog",
"Author:": "Auteur:",
"Auto Adjust Colors": "Automatisch kleuren aanpassen",
"Auto Kerning": "Automatisch letterafstand aanpassen",
"Average:": "Gemiddelde:",
"Backspace": "Rugpijn",
"Base": "Basis",
"Basic": "BASIS",
"Black and White": "Zwart en Wit",
"Blue": "Blauw",
"Blue channel:": "Blauw kanaal:",
"Blueprint": "Blauwdruk",
"Blur Radius:": "Vervagingsstraal:",
"Blur Tool": "Vervagingsgereedschap",
"Blur power:": "Vervagingskracht:",
"Borders": "Randen",
"Bottom": "Onderkant",
"Bottom to Top": "Van onder naar boven",
"Bounds:": "Grenzen:",
"Box": "Doos",
"Box Blur": "Doos vervagen",
"Box blur": "Doos vervagen",
"Brightness": "Helderheid",
"Brightness:": "Helderheid:",
"Bulge\/Pinch Tool": "Uitzetten\/knijpen gereedschap",
"Burn": "Branden",
"Can not animate 1 layer.": "Kan geen 1 laag animeren.",
"Can not find previous layer.": "Kan de vorige laag niet vinden.",
"Can not use this tool on current layer: image already takes all area.": "Kan dit gereedschap niet gebruiken op de huidige laag: de afbeelding neemt al het hele gebied in beslag.",
"Cancel": "Annuleren",
"Canvas Size": "Canvas grootte",
"Center": "Midden",
"Center x:": "Middelpunt x:",
"Center y:": "Middelpunt y:",
"Center:": "Midden:",
"Change Composition": "Compositie wijzigen",
"Change Layer Details": "Laagdetails wijzigen",
"Change Opacity": "Wijzig de dekking",
"Channel:": "Kanaal:",
"Circle": "Cirkel",
"Clarendon": "Clarendon",
"Clear": "Wissen",
"Clear Selection": "Selectie wissen",
"Clone Tool": "Kloon gereedschap",
"Clone count:": "Aantal klonen:",
"Clone tool disabled for resized image. Please rasterize first.": "Kloontool uitgeschakeld voor afbeelding met gewijzigd formaat. Gelieve eerst te rasteren.",
"Cloned edges": "Gekloonde randen",
"Close": "Dichtbij",
"Color #": "Kleur #",
"Color Corrections": "Kleurcorrecties",
"Color Palette": "Kleurenpalet",
"Color Zoom": "Kleurzoom",
"Color alpha value can not be zero.": "Kleur alfa-waarde kan niet nul zijn.",
"Color to Alpha": "Kleur naar Alpha",
"Color zoom": "Kleurzoom",
"Color:": "Kleur:",
"Colors": "Kleuren",
"Colors:": "Kleuren:",
"Common Filters": "Gemeenschappelijke filters",
"Composition": "Samenstelling",
"Composition:": "Samenstelling:",
"Content Fill": "Inhoud vullen",
"Contrast": "Contrast",
"Contrast:": "Contrast:",
"Convert layer to raster": "Converteer laag naar raster",
"Convert to Raster": "Converteren naar raster",
"Copy Selection": "Selectie kopiëren",
"Copy to Clipboard": "Kopiëren naar klembord",
"Courier": "Koerier",
"Crop Tool": "Bijsnijdgereedschap",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Bijsnijden op geroteerde laag wordt niet ondersteund. Converteer het naar raster om door te gaan.",
"Ctrl + C": "Ctrl+C",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl + V",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "Huidige",
"Current Color Preview": "Huidige kleurvoorbeeld",
"Custom": "Aangepast",
"Data URL": "Gegevens-URL",
"Data URL:": "Gegevens-URL:",
"Decrease": "Verminderen",
"Decrease Color Depth": "Kleurdiepte verminderen",
"Degree:": "Graad:",
"Del": "Del",
"Delete": "Verwijderen",
"Delete Selection": "Selectie verwijderen",
"Denoise": "Ruis verminderen",
"Desaturate Tool": "Ontzadigen gereedschap",
"Description:": "Beschrijving:",
"Deutsch": "Duits",
"Differences": "Verschillen",
"Differences Down": "Verschillen omlaag",
"Direction:": "Richting:",
"Dither": "Dither",
"Dithering:": "Dithering:",
"Dominant color:": "Dominante kleur:",
"Dot Screen": "Puntenscherm",
"Down": "Omlaag",
"Duplicate": "Dupliceren",
"Duplicate Layer": "Dupliceer laag",
"Duplicate layer": "Dubbele laag",
"Dutch": "Nederlands",
"Dynamic": "Dynamisch",
"Edge": "Rand",
"Edit": "Bewerken",
"Edit text...": "Tekst bewerken...",
"Effect browser": "Effectenbrowser",
"Effects": "Effecten",
"Effects browser": "Effectenbrowser",
"Email:": "E-mail:",
"Emboss": "In reliëf",
"Empty selection": "Lege selectie",
"Empty selection or type not image.": "Lege selectie of type geen afbeelding.",
"Enable autoresize:": "Automatisch aanpassen van formaat inschakelen:",
"End": "Einde",
"English": "Engels",
"English (UK)": "Engels (VK)",
"Enrich": "Verrijken",
"Enter": "Invoeren",
"Erase Tool": "Wismiddel",
"Erase on rotate object is disabled. Please rasterize first.": "Wissen bij roteren van object is uitgeschakeld. Gelieve eerst te rasteren.",
"Error": "Fout",
"Error connecting to service.": "Fout bij het verbinden met de service.",
"Error loading the list of fonts from Google.": "Fout bij het laden van de lijst met lettertypen van Google.",
"Error registering service worker": "Fout bij registreren van servicemedewerker",
"Error: can not find filter:": "Fout: kan filter niet vinden:",
"Error: can not find layer with id:": "Fout: kan laag met id niet vinden:",
"Error: missing details event target": "Fout: ontbrekend doelevenementdoel",
"Error: unknown layer type:": "Fout: onbekend laagtype:",
"Error: unsupported attribute type:": "Fout: niet-ondersteund attribuuttype:",
"Esc": "Esc",
"Escape": "Ontsnappen",
"Español": "Spaans",
"Expand edges": "Randen uitbreiden",
"Exponent:": "Exponent:",
"Export": "Exporteren",
"External": "Extern",
"Factor:": "Factor:",
"File": "Bestand",
"File name:": "Bestandsnaam:",
"File size:": "Bestandsgrootte:",
"Fill": "Vullen",
"Fill Tool": "Vulmiddel",
"Fit": "Passend maken",
"Fit Window": "Venster passend maken",
"Fit window": "Venster passen",
"Flatten Image": "Afbeelding afvlakken",
"Flip": "Omdraaien",
"FloydSteinberg-serpentine": "FloydSteinberg-serpentijn",
"Font": "Lettertype",
"Français": "Frans",
"Full HD, 1080p": "Volledig HD, 1080p",
"Full Screen": "Volledig scherm",
"Full layers data": "Volledige laaggegevens",
"Gap:": "Spleet:",
"Gaussian Blur": "Gaussische vervaging",
"Gif delay:": "Gif-vertraging:",
"Gingham": "Gingham",
"GitHub:": "GitHub:",
"Gradient Radius:": "Gradiëntradius:",
"Grains": "Korrels",
"Graphics Interchange Format": "Grafische uitwisselingsformaat",
"Gray": "Grijs",
"Grayscale": "Grijstinten",
"Greek": "Grieks",
"Green": "Groen",
"Green channel:": "Groen kanaal:",
"Greyscale:": "Grijstinten:",
"Grid": "Raster",
"Grid on\/off": "Raster aan\/uit",
"Guides": "Gidsen",
"Guides enabled.": "Gidsen ingeschakeld.",
"H Radius:": "H Radius:",
"H. Align:": "H. Uitlijnen:",
"Heatmap": "Warmtekaart",
"Height (%):": "Hoogte (%):",
"Height:": "Hoogte:",
"Help": "Help",
"Helvetica": "Helvetica",
"Hermite": "Hermite",
"Hex": "Hex",
"Hide": "Verbergen",
"Histogram": "Histogram",
"Histogram:": "Histogram:",
"Home": "Start",
"Horizontal": "Horizontaal",
"Horizontal Alignment": "Horizontale uitlijning",
"Horizontal blur:": "Horizontale vervaging:",
"Horizontal:": "Horizontaal:",
"Hue": "Tint",
"Hue Rotate": "Hue Rotate",
"Hue:": "Tint:",
"Image": "Afbeelding",
"Image data with multi-layers. Can be opened using miniPaint -": "Afbeeldingsgegevens met meerdere lagen. Kan worden geopend met miniPaint -",
"Impact": "Impact",
"In proportion:": "In proportie:",
"Increase": "Verhogen",
"Information": "Informatie",
"Inkwell": "Inktpot",
"Insert": "Invoegen",
"Insert guides": "Gidsen plaatsen",
"Insert new layer": "Nieuwe laag invoegen",
"Instagram Filters": "Instagram Filters",
"Invalid Hex Code": "Ongeldige Hex Code",
"Italiano": "Italiaans",
"JPG\/JPEG Format": "JPG\/JPEG Formaat",
"Kerning:": "Kerning:",
"Key-Points": "Belangrijke Punten",
"KeyU": "SleutelU",
"Keyboard Shortcuts": "Sneltoetsen",
"Keyword:": "Sleutelwoord:",
"Lanczos": "Lanczos",
"Landscape": "Landschap",
"Language": "Taal",
"Last modified": "Laatst gewijzigd",
"Layer": "Laag",
"Layer details": "Laagdetails",
"Layer is empty.": "Laag is leeg.",
"Layer is not compatible with resize": "Laag is niet compatibel met formaatwijziging",
"Layer is vector, convert it to raster to apply this tool.": "Laag is vector, converteer deze naar raster om dit gereedschap toe te passen.",
"Layers": "Lagen",
"Layers:": "Lagen:",
"Layout:": "Indeling:",
"Left": "Links",
"Left to Right": "Links naar Rechts",
"Level:": "Niveau:",
"Levels:": "Niveaus:",
"Lietuvių": "Lietuvių",
"Lo-fi": "Lo-fi",
"Luminance:": "Luminantie:",
"Luminosity": "Luminositeit",
"Magic Eraser Tool": "Tovergummi",
"Merge Down": "Samenvoegen Omlaag",
"Merge Layers": "Lagen Samenvoegen",
"Merged": "Samengevoegd",
"Metrics": "Metrieken",
"Middle": "Midden",
"Missing at least 1 size parameter.": "Minstens 1 grootteparameter ontbreekt.",
"Missing permissions to write to Clipboard.cc": "Machtigingen ontbreken om naar Clipboard.cc te schrijven",
"Mode:": "Modus:",
"Module function not found.": "Modulefunctie niet gevonden.",
"Modules class not found:": "Modulesklasse niet gevonden:",
"Monospace": "Monospace",
"Mosaic": "Mozaïek",
"Mouse:": "Muis:",
"Move": "Verplaatsen",
"Move Layer": "Laag Verplaatsen",
"Move layer down": "Verplaats laag naar beneden",
"Move layer up": "Verplaats laag naar boven",
"Name:": "Naam:",
"Negative": "Negatief",
"New": "Nieuw",
"New Bezier Layer": "Nieuwe Bezier-laag",
"New Brush Layer": "Nieuwe Kwastlaag",
"New Ellipse Layer": "Nieuwe Ellipslaag",
"New File": "Nieuw Bestand",
"New Gradient Layer": "Nieuwe Gradiëntlaag",
"New Layer": "Nieuwe Laag",
"New Line Layer": "Nieuwe Lijnlaag",
"New Pencil Layer": "Nieuwe Potloodlaag",
"New Polygon Layer": "Nieuwe veelhoeklaag",
"New Rectangle Layer": "Nieuwe Rechthoekige Laag",
"New Text Layer": "Nieuwe Tekstlaag",
"New file": "Nieuw bestand",
"New from Selection": "Nieuw vanuit Selectie",
"New layer": "Nieuwe laag",
"Next": "Volgende",
"Night Vision": "Nachtkijker",
"None": "Geen",
"Nothing is selected.": "Niets is geselecteerd.",
"Offset X:": "Verschuiving X:",
"Offset Y:": "Verschuiving Y:",
"Oil": "Olie",
"Ok": "Oké",
"Online image editor.": "Online afbeelding editor",
"Opacity": "Dekking",
"Opacity:": "Dekking:",
"Open": "Openen",
"Open Data URL": "Open Data-URL",
"Open Directory": "Open Map",
"Open File": "Open Bestand",
"Open File Data URL": "Open Bestand Data-URL",
"Open File URL": "Open Bestand-URL",
"Open File Webcam": "Open Bestand Webcam",
"Open Image": "Open Afbeelding",
"Open JSON File": "Open JSON Bestand",
"Open Test Template": "Open Test Sjabloon",
"Open URL": "Open URL",
"Open data URL": "Open data-URL",
"Open from Webcam": "Open van Webcam",
"Original Size": "Origineel Formaat",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Afbeelding converteren naar SVG",
"PageDown": "Pagina Omlaag",
"PageUp": "Pagina Omhoog",
"Palette": "Palet",
"Parameter #1:": "Parameter #1:",
"Parameter #2:": "Parameter #2:",
"Paste": "Plakken",
"Pencil": "Potlood",
"Percentage:": "Percentage:",
"Pixels:": "Pixels:",
"Placeholder comment for color channels": "Plaatsvervangend commentaar voor kleurkanalen",
"Placeholder comment for color picker": "Plaatsvervangend commentaar voor kleurkiezer",
"Placeholder comment for color swatches": "Plaatsvervangend commentaar voor kleurenstaaltjes",
"Portable Network Graphics": "Portable Network Graphics",
"Portrait": "Portret",
"Português": "Portugees",
"Position:": "Positie:",
"Power:": "Kracht:",
"Preview": "Voorbeeld",
"Previous": "Vorige",
"Previous layer must be image, convert it to raster to apply this tool.": "De vorige laag moet een afbeelding zijn, zet deze om naar raster om deze tool toe te passen.",
"Print": "Afdrukken",
"Quality:": "Kwaliteit:",
"Quick Load": "Snel laden",
"Quick Save": "Snel opslaan",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Verwijder Achtergrond van Afbeelding",
"Radial": "Radiaal",
"Radial gradient": "Radiale gradiënt",
"Radius:": "Straal:",
"Range:": "Bereik:",
"Red": "Rood",
"Red channel:": "Rood kanaal:",
"Redo": "Opnieuw",
"Remove all": "Verwijder alles",
"Rename": "Hernoemen",
"Rename Layer": "Laag hernoemen",
"Rendered with errors.": "Weergegeven met fouten.",
"Rendering...": "Renderen...",
"Replace Color": "Kleur vervangen",
"Replace color": "Kleur vervangen",
"Replacement:": "Vervanging:",
"Report Issues": "Problemen rapporteren",
"Reset": "Resetten",
"Resize": "Formaat wijzigen",
"Resize Boundary": "Formaat van grens wijzigen",
"Resize Layer": "Formaat van laag wijzigen",
"Resize Layers": "Formaat van lagen wijzigen",
"Resize Text Layer": "Formaat van tekstlaag wijzigen",
"Resized as background": "Hernoemd als achtergrond",
"Resized:": "Formaat gewijzigd:",
"Resolution:": "Resolutie:",
"Restore Alpha": "Alfa herstellen",
"Right": "Rechts",
"Right angle:": "Rechte hoek:",
"Right to Left": "Van rechts naar links",
"Rotate": "Roteren",
"Rotate Layer": "Laag roteren",
"Rotate is not supported on this type of object. Convert to raster?": "Roteren wordt niet ondersteund voor dit type object. Omzetten naar raster?",
"Rotate left": "Links roteren",
"Rotate:": "Roteren:",
"Ruler": "Liniaal",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - Afbeeldingen comprimeren en vergelijken",
"Saturate": "Verzadigen",
"Saturation": "Verzadiging",
"Saturation:": "Verzadiging:",
"Save As": "Opslaan als",
"Save As Data URL": "Opslaan als gegevens-URL",
"Save as": "Opslaan als",
"Save as type:": "Opslaan als type:",
"Save layers:": "Lagen opslaan:",
"Scaling up is not supported in Hermite, using Lanczos.": "Opschalen wordt niet ondersteund in Hermite, Lanczos wordt gebruikt.",
"Scroll down": "Omlaag scrollen",
"Scroll up": "Omhoog scrollen",
"Search": "Zoeken",
"Search Images": "Afbeeldingen zoeken",
"Search for Font": "Zoek naar lettertype",
"Search:": "Zoekopdracht:",
"Select All": "Alles selecteren",
"Select Text Layer": "Tekstlaag selecteren",
"Select object tool": "Objectgereedschap selecteren",
"Selected": "Geselecteerd",
"Selection Tool": "Selectiegereedschap",
"Sensitivity:": "Gevoeligheid:",
"Separated": "Gescheiden",
"Separated (original types)": "Gescheiden (originele typen)",
"Sepia": "Sepia",
"Set Image Size": "Afbeeldingsgrootte instellen",
"Settings": "Instellingen",
"Shadow": "Schaduw",
"Shapes": "Vormen",
"Shapes (H)": "Vormen (H)",
"Sharpen": "Verscherpen",
"Sharpen Tool": "Verscherpgereedschap",
"Sharpen:": "Verscherpen:",
"Shift + S": "Shift + S",
"Shortcut Key:": "Sneltoets:",
"Show": "Show",
"Show \/ Hide": "Tonen \/ Verbergen",
"Show file size:": "Toon bestandsgrootte:",
"Simple": "Eenvoudig",
"Size is too big, max": "Grootte is te groot, maximaal",
"Size:": "Grootte:",
"Skip - layer must be image.": "Overslaan - laag moet een afbeelding zijn.",
"Solarize": "Solariseren",
"Sorry, cold not load getUserMedia() data:": "Sorry, kon getUserMedia() gegevens niet laden:",
"Sorry, image could not be loaded.": "Sorry, afbeelding kon niet worden geladen.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Sorry, afbeelding kon niet worden geladen. Probeer de afbeelding te kopiëren en te plakken.",
"Sorry, image is too big, max 5 MB.": "Sorry, afbeelding is te groot, maximaal 5 MB.",
"Source coordinates saved.": "Broncoördinaten opgeslagen.",
"Source is empty, right click on image or use long press to save source position.": "Bron is leeg, klik met de rechtermuisknop op de afbeelding of gebruik een lange druk om de bronpositie op te slaan.",
"Sprites": "Sprites",
"Square": "Vierkant",
"Stream:": "Stroom:",
"Strength:": "Kracht:",
"Strict": "Strikt",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - Comprimeer PNG en JPEG",
"Tab": "Tabblad",
"Tag Image File Format": "Tag afbeelding bestandsformaat",
"Tahoma": "Tahoma",
"Target:": "Doel:",
"The quick brown fox jumps over the lazy dog.": "De snelle bruine vos springt over de luie hond heen.",
"There": "Daar",
"There are no layers behind.": "Er zijn geen lagen achter.",
"There is only 1 layer.": "Er is slechts 1 laag.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Deze laag moet een afbeelding bevatten. Converteer deze alstublieft naar raster om deze tool toe te passen.",
"Tilt Shift": "Kantelverschuiving",
"Times New Roman": "Times New Roman",
"Toaster": "Broodrooster",
"Toggle": "Schakelen",
"Toggle Color Channels": "Schakel kleurkanalen",
"Toggle Color Picker": "Schakel kleurkiezer",
"Toggle Menu": "Schakel menu",
"Toggle Swatches": "Schakel kleurstalen",
"Tools": "Gereedschappen",
"Top": "Bovenkant",
"Top to Bottom": "Van boven naar beneden",
"Total pixels:": "Totaal aantal pixels:",
"Translate": "Vertalen",
"Translate Layer": "Vertaal laag",
"Translate error, can not find dictionary:": "Vertaalfout, kan woordenboek niet vinden:",
"Transparent:": "Transparant:",
"Trim": "Bijsnijden",
"Trim Layers": "Bijsnijden van lagen",
"Trim borders:": "Bijsnijden van randen:",
"Trim layer:": "Bijsnijden van laag:",
"Trim white color?": "Witte kleur bijsnijden?",
"Type:": "Type:",
"Türkçe": "Türkçe",
"Undo": "Ongedaan maken",
"Unique colors:": "Unieke kleuren:",
"Up": "Omhoog",
"Update": "Update",
"Update Brush Layer": "Werk penseel laag bij",
"Update Pencil Layer": "Werk potlood laag bij",
"Update guides": "Gidsen bijwerken",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Gebruik Ctrl+V sneltoets om te plakken vanaf het Klembord.",
"V Radius:": "V Straal:",
"V. Align:": "V. Uitlijnen:",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "Versie:",
"Vertical": "Verticaal",
"Vertical Alignment": "Verticale uitlijning",
"Vertical blur:": "Verticaal vervagen:",
"Vertical:": "Verticaal:",
"Vibrance": "Levendigheid",
"View": "Weergave",
"Vignette": "Vignet",
"ViliusL": "ViliusL",
"Vintage": "Vintage",
"Webcam": "Webcam",
"Webcam #": "Webcam #",
"Website:": "Website:",
"Weppy File Format": "Weppy bestandsformaat",
"Width (%):": "Breedte (%):",
"Width:": "Breedte:",
"Windows Bitmap": "Windows Bitmap",
"Word": "Woord",
"Word + Letter": "Woord + Letter",
"Wrap At:": "Omzetten bij:",
"Wrap:": "Omzetten:",
"Wrong dimensions": "Verkeerde afmetingen",
"Wrong file type, must be image or json.": "Verkeerd bestandstype, moet afbeelding of json zijn.",
"X end:": "X eind:",
"X position:": "X positie:",
"X start:": "X start:",
"X-Pro II": "X-Pro II",
"Y end:": "Y eind:",
"Y position:": "Y positie:",
"Y start:": "Y start:",
"You can also drag and drop items into browser.": "U kunt ook items naar de browser slepen en neerzetten.",
"Your browser does not support canvas or JavaScript is not enabled.": "Uw browser ondersteunt geen canvas of JavaScript is niet ingeschakeld.",
"Your browser does not support this format.": "Uw browser ondersteunt dit formaat niet.",
"Your search did not match any images.": "Uw zoekopdracht leverde geen overeenkomende afbeeldingen op.",
"Zoom": "Zoomen",
"Zoom Blur": "Zoomvervaging",
"Zoom In": "Inzoomen",
"Zoom Out": "Uitzoomen",
"Zoom blur": "Zoomvervaging",
"Zoom in": "Inzoomen",
"Zoom out": "Uitzoomen",
"Zoom:": "Zoom:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Ocorreu um problema ao remover o histórico de desfazer. isto",
"About": "Sobre",
"Active": "Ativo",
"Aden": "Aden",
"Advanced": "Avançado",
"All": "Todos",
"Alpha": "Alfa",
"Alpha:": "Alfa:",
"Anonymous": "Anônimo",
"Anti aliasing": "Anti-aliasing",
"Application markup may have changed,": "A marcação do aplicativo pode ter mudado,",
"Arial": "Arial",
"Arrow": "Flecha",
"ArrowDown": "Seta para baixo",
"ArrowLeft": "Seta para a esquerda",
"ArrowRight": "Seta para a direita",
"ArrowUp": "Seta para cima",
"Author:": "Autor:",
"Auto Adjust Colors": "Cores de ajuste automático",
"Auto Kerning": "Auto Kerning",
"Average:": "Média:",
"Backspace": "Backspace",
"Base": "Base",
"Basic": "Básico",
"Black and White": "Preto e branco",
"Blue": "Azul",
"Blue channel:": "Canal azul:",
"Blueprint": "Blueprint",
"Blur Radius:": "Raio de desfoque:",
"Blur Tool": "Ferramenta de desfoque",
"Blur power:": "Intensidade do desfoque:",
"Borders": "Bordas",
"Bottom": "Inferior",
"Bottom to Top": "De baixo para cima",
"Bounds:": "Limites:",
"Box": "Caixa",
"Box Blur": "Desfoque de caixa",
"Box blur": "Desfoque de caixa",
"Brightness": "Brilho",
"Brightness:": "Brilho:",
"Bulge\/Pinch Tool": "Ferramenta Bulge \/ Pinch",
"Burn": "Queimar",
"Can not animate 1 layer.": "Não é possível animar 1 camada.",
"Can not find previous layer.": "Não é possível encontrar a camada anterior.",
"Can not use this tool on current layer: image already takes all area.": "Não é possível usar esta ferramenta na camada atual: a imagem já ocupa toda a área.",
"Cancel": "Cancelar",
"Canvas Size": "Tamanho da tela",
"Center": "Centro",
"Center x:": "Centro x:",
"Center y:": "Centro y:",
"Center:": "Centro:",
"Change Composition": "Alterar composição",
"Change Layer Details": "Alterar os detalhes da camada",
"Change Opacity": "Alterar opacidade",
"Channel:": "Canal:",
"Circle": "Círculo",
"Clarendon": "Clarendon",
"Clear": "Limpar",
"Clear Selection": "Limpar seleção",
"Clone Tool": "Ferramenta Clone",
"Clone count:": "Contagem de clones:",
"Clone tool disabled for resized image. Please rasterize first.": "Ferramenta de clonagem desativada para imagem redimensionada. Por favor, rasterize primeiro.",
"Cloned edges": "Bordas clonadas",
"Close": "Fechar",
"Color #": "Cor #",
"Color Corrections": "Correções de cores",
"Color Palette": "Paleta de cores",
"Color Zoom": "Zoom de cor",
"Color alpha value can not be zero.": "O valor alfa da cor não pode ser zero.",
"Color to Alpha": "Cor para alfa",
"Color zoom": "Zoom de cor",
"Color:": "Cor:",
"Colors": "Cores",
"Colors:": "Cores:",
"Common Filters": "Filtros Comuns",
"Composition": "Composição",
"Composition:": "Composição:",
"Content Fill": "Preenchimento de conteúdo",
"Contrast": "Contraste",
"Contrast:": "Contraste:",
"Convert layer to raster": "Rasterizar camada",
"Convert to Raster": "Rasterizar",
"Copy Selection": "Seleção de cópia",
"Copy to Clipboard": "Copiar para área de transferência",
"Courier": "Courier",
"Crop Tool": "Ferramenta de corte",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "O corte na camada girada não é compatível. Rasterize a camada para continuar.",
"Ctrl + C": "Ctrl + C",
"Ctrl+A": "Ctrl+A",
"Ctrl+C": "Ctrl+C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl+V",
"Ctrl+Y": "Ctrl+Y",
"Ctrl+Z": "Ctrl+Z",
"Current": "Atual",
"Current Color Preview": "Pré-visualização da cor atual",
"Custom": "personalizado",
"Data URL": "URL de dados",
"Data URL:": "URL de dados:",
"Decrease": "Diminuir",
"Decrease Color Depth": "Diminuir a profundidade de cor",
"Degree:": "Grau:",
"Del": "Del",
"Delete": "Excluir",
"Delete Selection": "Excluir seleção",
"Denoise": "Reduzir ruído",
"Desaturate Tool": "Ferramenta de dessaturação",
"Description:": "Descrição:",
"Deutsch": "Deutsch",
"Differences": "Diferenças",
"Differences Down": "Diferenças para baixo",
"Direction:": "Direção:",
"Dither": "Dither",
"Dithering:": "Dithering:",
"Dominant color:": "Cor dominante:",
"Dot Screen": "Tela de ponto",
"Down": "Abaixo",
"Duplicate": "Duplicado",
"Duplicate Layer": "Duplicar Camada",
"Duplicate layer": "Duplicar Camada",
"Dynamic": "Dinâmico",
"Edge": "Borda",
"Edit": "Editar",
"Edit text...": "Editar texto...",
"Effect browser": "Buscador de efeitos",
"Effects": "Efeitos",
"Effects browser": "Buscador de efeitos",
"Email:": "Email:",
"Emboss": "Em relevo",
"Empty selection": "Seleção vazia",
"Empty selection or type not image.": "Seleção vazia ou tipo selecionado não é uma imagem.",
"Enable autoresize:": "Ativar redimensionamento automático:",
"End": "Fim",
"English": "English",
"English (UK)": "English (UK)",
"Enrich": "Enriquecer",
"Enter": "Entrar",
"Erase Tool": "Ferramenta Apagar",
"Erase on rotate object is disabled. Please rasterize first.": "Apagar ao girar o objeto está desativado. Por favor, rasterize o objeto primeiro.",
"Error": "Erro",
"Error connecting to service.": "Erro ao conectar-se ao serviço.",
"Error loading the list of fonts from Google.": "Erro ao carregar a lista de fontes do Google.",
"Error registering service worker": "Erro ao registrar o service worker",
"Error: can not find filter:": "Erro: não foi possível encontrar o filtro:",
"Error: can not find layer with id:": "Erro: não foi possível encontrar camada com id:",
"Error: missing details event target": "Erro: faltam detalhes do alvo do evento",
"Error: unknown layer type:": "Erro: tipo de camada desconhecido:",
"Error: unsupported attribute type:": "Erro: tipo de atributo não suportado:",
"Esc": "Esc",
"Escape": "Escapar",
"Español": "Español",
"Expand edges": "Expandir bordas",
"Exponent:": "Expoente:",
"Export": "Exportar",
"External": "Externo",
"Factor:": "Fator:",
"File": "Arquivo",
"File name:": "Nome do arquivo:",
"File size:": "Tamanho do arquivo:",
"Fill": "Preencher",
"Fill Tool": "Ferramenta de Preenchimento",
"Fit": "Ajustar",
"Fit Window": "Ajuste de Janela",
"Fit window": "Ajustar janela",
"Flatten Image": "Achatar imagem",
"Flip": "Giro",
"FloydSteinberg-serpentine": "FloydSteinberg-serpentine",
"Font": "Fonte",
"Français": "Français",
"Full HD, 1080p": "Full HD, 1080p",
"Full Screen": "Tela cheia",
"Full layers data": "Dados de camadas completas",
"Gap:": "Espaçamento:",
"Gaussian Blur": "Desfoque Gaussiano",
"Gif delay:": "Atraso do GIF:",
"Gingham": "Tecido de algodão",
"GitHub:": "GitHub:",
"Gradient Radius:": "Raio do Gradiente:",
"Grains": "Grãos",
"Graphics Interchange Format": "Formato de intercâmbio de gráficos",
"Gray": "Cinza",
"Grayscale": "Escala de cinza",
"Greek": "Ελληνικά (Greek)",
"Green": "Verde",
"Green channel:": "Canal verde:",
"Greyscale:": "Escala de cinza:",
"Grid": "Grade",
"Grid on\/off": "Grades Ligado \/ Desligado",
"Guides": "Guias",
"Guides enabled.": "Guias ativados.",
"H Radius:": "Raio H.",
"H. Align:": "Alinhamento H.",
"Heatmap": "Mapa de calor",
"Height (%):": "Altura (%):",
"Height:": "Altura:",
"Help": "Ajuda",
"Helvetica": "helvetica",
"Hermite": "Hermite",
"Hex": "Hex",
"Hide": "Ocultar",
"Histogram": "Histograma",
"Histogram:": "Histograma:",
"Home": "Início",
"Horizontal": "Horizontal",
"Horizontal Alignment": "Alinhamento horizontal",
"Horizontal blur:": "Desfoque horizontal:",
"Horizontal:": "Horizontal:",
"Hue": "Matiz",
"Hue Rotate": "Rotação de Matiz",
"Hue:": "Matiz:",
"Image": "Imagem",
"Image data with multi-layers. Can be opened using miniPaint -": "Dados de imagem com várias camadas. Pode ser aberto usando o miniPaint -",
"Impact": "Impact",
"In proportion:": "Na proporção:",
"Increase": "Aumentar",
"Information": "Informação",
"Inkwell": "Tinteiro",
"Insert": "Inserir",
"Insert guides": "Inserir guias",
"Insert new layer": "Inserir nova camada",
"Instagram Filters": "Filtros de Instagram",
"Invalid Hex Code": "Código Hex inválido",
"Italiano": "Italiano",
"JPG\/JPEG Format": "Formato JPG \/ JPEG",
"Kerning:": "Kerning:",
"Key-Points": "Pontos-chave",
"KeyU": "KeyU",
"Keyboard Shortcuts": "Atalhos de teclado",
"Keyword:": "Palavra-chave:",
"Lanczos": "Lanczos",
"Landscape": "Paisagem",
"Language": "Idioma",
"Last modified": "Última modificação",
"Layer": "Camada",
"Layer details": "Detalhes da camada",
"Layer is empty.": "A camada está vazia.",
"Layer is not compatible with resize": "Camada não é compatível com redimensionamento",
"Layer is vector, convert it to raster to apply this tool.": "A camada é um vetor, rasterize-a para usar esta ferramenta.",
"Layers": "Camadas",
"Layers:": "Camadas:",
"Layout:": "Disposição:",
"Left": "Esquerda",
"Left to Right": "Da esquerda para direita",
"Level:": "Nível:",
"Levels:": "Níveis:",
"Lietuvių": "Lietuvių",
"Lo-fi": "Lo-fi",
"Luminance:": "Luminância:",
"Luminosity": "Luminosidade",
"Magic Eraser Tool": "Ferramenta de borracha mágica",
"Merge Down": "Mesclar para Baixo",
"Merge Layers": "Mesclar Camadas",
"Merged": "Mesclado",
"Metrics": "Métricas",
"Middle": "Meio",
"Missing at least 1 size parameter.": "Falta pelo menos 1 parâmetro de tamanho.",
"Missing permissions to write to Clipboard.cc": "Permissões ausentes para gravar em Clipboard.cc",
"Mode:": "Modo:",
"Module function not found.": "Função do módulo não encontrada.",
"Modules class not found:": "Classe de módulos não encontrada:",
"Monospace": "Monospace",
"Mosaic": "mosaico",
"Mouse:": "Mouse:",
"Move": "Mover",
"Move Layer": "Mover Camada",
"Move layer down": "Mover camada para baixo",
"Move layer up": "Mover camada para cima",
"Name:": "Nome:",
"Negative": "Negativo",
"New": "Novo",
"New Bezier Layer": "Nova camada de Bézier",
"New Brush Layer": "Nova Camada de Pincel",
"New Ellipse Layer": "Nova Camada de Elipse",
"New File": "Novo arquivo",
"New Gradient Layer": "Nova Camada de Gradiente",
"New Layer": "Nova camada",
"New Line Layer": "Nova Camada de Linha",
"New Pencil Layer": "Nova Camada de Lápis",
"New Polygon Layer": "Nova camada de polígono",
"New Rectangle Layer": "Nova Camada de Retângulo",
"New Text Layer": "Nova Camada de Texto",
"New file": "Novo arquivo",
"New from Selection": "Novo da seleção",
"New layer": "Nova camada",
"Next": "Próximo",
"Night Vision": "Visão noturna",
"None": "Nenhum",
"Nothing is selected.": "Não há nada selecionado.",
"Offset X:": "Deslocamento X:",
"Offset Y:": "Deslocamento Y:",
"Oil": "Óleo",
"Ok": "Ok",
"Online image editor.": "Editor de imagens online.",
"Opacity": "Opacidade",
"Opacity:": "Opacidade:",
"Open": "Aberto",
"Open Data URL": "Abrir URL de dados",
"Open Directory": "Diretório aberto",
"Open File": "Abrir arquivo",
"Open File Data URL": "URL de dados de arquivo aberta",
"Open File URL": "URL do arquivo aberta",
"Open File Webcam": "Abrir arquivo da webcam",
"Open Image": "Abrir Imagem",
"Open JSON File": "Abrir arquivo JSON",
"Open Test Template": "Abrir modelo de teste",
"Open URL": "Abrir URL",
"Open data URL": "Abrir URL de dados",
"Open from Webcam": "Abrir na webcam",
"Original Size": "Tamanho original",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Converter imagem para SVG",
"PageDown": "PageDown",
"PageUp": "PageUp",
"Palette": "Paleta",
"Parameter #1:": "Parâmetro #1:",
"Parameter #2:": "Parâmetro #2:",
"Paste": "Colar",
"Pencil": "Lápis",
"Percentage:": "Porcentagem:",
"Pixels:": "Píxeis:",
"Placeholder comment for color channels": "Comentário reservado para canais de cores",
"Placeholder comment for color picker": "Comentário reservado para posição para o seletor de cores",
"Placeholder comment for color swatches": "Comentário reservado para amostras de cores",
"Portable Network Graphics": "Gráficos Portáteis de Rede",
"Portrait": "Retrato",
"Português": "Português",
"Position:": "Posição:",
"Power:": "Poder:",
"Preview": "Pré-visualização",
"Previous": "Anterior",
"Previous layer must be image, convert it to raster to apply this tool.": "A camada anterior deve ser uma imagem, rasterize-a para aplicar esta ferramenta.",
"Print": "Imprimir",
"Quality:": "Qualidade:",
"Quick Load": "Carregamento Rápido",
"Quick Save": "Salvamento Rápido",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Remover fundo da imagem",
"Radial": "Radial",
"Radial gradient": "Gradiente radial",
"Radius:": "Raio:",
"Range:": "Alcance:",
"Red": "Vermelho",
"Red channel:": "Canal vermelho:",
"Redo": "Refazer",
"Remove all": "Remover tudo",
"Rename": "Renomear",
"Rename Layer": "Renomear Camada",
"Rendered with errors.": "Renderizado com erros.",
"Rendering...": "Renderizando...",
"Replace Color": "Substituir Cor",
"Replace color": "Substitua cor",
"Replacement:": "Substituição:",
"Report Issues": "Relatar problemas",
"Reset": "Redefinir",
"Resize": "Redimensionar",
"Resize Boundary": "Redimensionar limite",
"Resize Layer": "Camada de redimensionamento",
"Resize Layers": "Camadas de redimensionamento",
"Resize Text Layer": "Redimensionar Camada de Texto",
"Resized as background": "Redimensionado como plano de fundo",
"Resized:": "Redimensionado:",
"Resolution:": "Resolução:",
"Restore Alpha": "Restaurar alfa",
"Right": "Direita",
"Right angle:": "Ângulo direito:",
"Right to Left": "Direita para a esquerda",
"Rotate": "Rotacionar",
"Rotate Layer": "Rotacionar Camada",
"Rotate is not supported on this type of object. Convert to raster?": "Rotacionar não é suportado neste tipo de objeto. Rasterizar objeto?",
"Rotate left": "Rotacionar à esquerda",
"Rotate:": "Rotacionar:",
"Ruler": "Régua",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - Comprimir e comparar imagens",
"Saturate": "Saturar",
"Saturation": "Saturação",
"Saturation:": "Saturação:",
"Save As": "Salvar como",
"Save As Data URL": "Salvar como URL de dados",
"Save as": "Salvar como",
"Save as type:": "Salvar como tipo:",
"Save layers:": "Salvar camadas:",
"Scaling up is not supported in Hermite, using Lanczos.": "O aumento de escala não é compatível com Hermite, usando Lanczos.",
"Scroll down": "Deslize para baixo",
"Scroll up": "Deslize para cima",
"Search": "Pesquisar",
"Search Images": "Pesquisar Imagens",
"Search for Font": "Pesquisar Fonte",
"Search:": "Pesquisar:",
"Select All": "Selecionar tudo",
"Select Text Layer": "Selecione Camada de Texto",
"Select object tool": "Selecione a ferramenta de objeto",
"Selected": "Selecionado",
"Selection Tool": "Ferramenta de Seleção",
"Sensitivity:": "Sensibilidade:",
"Separated": "Separados",
"Separated (original types)": "Separados (tipos originais)",
"Sepia": "Sépia",
"Set Image Size": "Definir tamanho da imagem",
"Settings": "Configurações",
"Shadow": "Sombra",
"Shapes": "Formas",
"Shapes (H)": "Formas (H)",
"Sharpen": "Afiar",
"Sharpen Tool": "Ferramenta de Afiar",
"Sharpen:": "Afiar:",
"Shift + S": "Shift + S",
"Shortcut Key:": "Tecla de atalho:",
"Show": "Mostrar",
"Show \/ Hide": "Mostrar \/ Ocultar",
"Show file size:": "Mostrar tamanho do arquivo:",
"Simple": "Simples",
"Size is too big, max": "O tamanho é muito grande, máximo",
"Size:": "Tamanho:",
"Skip - layer must be image.": "Pular - camada deve ser uma imagem.",
"Solarize": "Solarize",
"Sorry, cold not load getUserMedia() data:": "Desculpe, não foi possível carregar dados de getUserMedia():",
"Sorry, image could not be loaded.": "Desculpe, não foi possível carregar a imagem.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Desculpe, a imagem não pôde ser carregada. Tente copiar a imagem e cole-a.",
"Sorry, image is too big, max 5 MB.": "Desculpe, a imagem é muito grande, ultrapassa o máximo permitido de 5 MB.",
"Source coordinates saved.": "Coordenadas da fonte salvas.",
"Source is empty, right click on image or use long press to save source position.": "A fonte está vazia, clique com o botão direito na imagem ou pressione e segure para salvar a posição da fonte.",
"Sprites": "Sprites",
"Square": "Quadrado",
"Stream:": "Transmissão:",
"Strength:": "Força:",
"Strict": "Estrito",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - Compactar PNG e JPEG",
"Tab": "Aba",
"Tag Image File Format": "Formato de Arquivo de Imagem Tag",
"Tahoma": "Tahoma",
"Target:": "Alvo:",
"The quick brown fox jumps over the lazy dog.": "A rápida raposa marrom salta sobre o cachorro preguiçoso.",
"There": "Lá",
"There are no layers behind.": "Não há camadas atrás.",
"There is only 1 layer.": "Existe apenas uma camada.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Esta camada deve ser uma imagem, rasterize-a para aplicar esta ferramenta.",
"Tilt Shift": "Desvio de Inclinação",
"Times New Roman": "Times New Roman",
"Toaster": "Torradeira",
"Toggle": "Alternar",
"Toggle Color Channels": "Alternar canais de cores",
"Toggle Color Picker": "Alternar seletor de cores",
"Toggle Menu": "Alternar menu",
"Toggle Swatches": "Alternar amostras",
"Tools": "Ferramentas",
"Top": "Topo",
"Top to Bottom": "De cima para baixo",
"Total pixels:": "Total de pixels:",
"Translate": "Traduzir",
"Translate Layer": "Traduzir Camada",
"Translate error, can not find dictionary:": "Erro de tradução, não foi possível encontrar dicionários:",
"Transparent:": "Transparente:",
"Trim": "Aparar",
"Trim Layers": "Aparar Camadas",
"Trim borders:": "Aparar Bordas:",
"Trim layer:": "Aparar camada:",
"Trim white color?": "Aparar cor branca?",
"Type:": "Tipo:",
"Türkçe": "Türkçe",
"Undo": "Desfazer",
"Unique colors:": "Cores únicas:",
"Up": "Acima",
"Update": "Atualizar",
"Update Brush Layer": "Atualizar Camada de Pincel",
"Update Pencil Layer": "Atualizar Camada de Lápis",
"Update guides": "Atualizar guias",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Use o atalho de teclado Ctrl + V para colar da área de transferência.",
"V Radius:": "Raio V.",
"V. Align:": "Alinhamento V.",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "Versão:",
"Vertical": "Vertical",
"Vertical Alignment": "Alinhamento vertical",
"Vertical blur:": "Desfoque vertical:",
"Vertical:": "Vertical:",
"Vibrance": "Vibração",
"View": "Visualizar",
"Vignette": "Vinheta",
"ViliusL": "ViliusL",
"Vintage": "Vintage",
"Webcam": "Webcam",
"Webcam #": "Webcam #",
"Website:": "Website:",
"Weppy File Format": "Formato de arquivo Weppy",
"Width (%):": "Largura (%):",
"Width:": "Largura:",
"Windows Bitmap": "Bitmap do Windows",
"Word": "Palavra",
"Word + Letter": "Palavra + Letra",
"Wrap At:": "Embrulhar em:",
"Wrap:": "Embrulho:",
"Wrong dimensions": "Dimensões erradas",
"Wrong file type, must be image or json.": "Tipo de arquivo errado, deve ser um arquivo do tipo imagem ou json.",
"X end:": "X final:",
"X position:": "Posição X:",
"X start:": "X inicial:",
"X-Pro II": "X-Pro II",
"Y end:": "Y final:",
"Y position:": "Posição Y:",
"Y start:": "Y inicial:",
"You can also drag and drop items into browser.": "Você também pode arrastar e soltar itens no navegador.",
"Your browser does not support canvas or JavaScript is not enabled.": "Seu navegador não é compatível com o HTML CANVAS ou o JavaScript não está habilitado.",
"Your browser does not support this format.": "Seu navegador não suporta este formato.",
"Your search did not match any images.": "Sua pesquisa não corresponde a nenhuma imagem.",
"Zoom": "Zoom",
"Zoom Blur": "Desfoque de zoom",
"Zoom In": "Mais Zoom",
"Zoom Out": "Menos zoom",
"Zoom blur": "Desfoque de zoom",
"Zoom in": "Mais Zoom",
"Zoom out": "Menos zoom",
"Zoom:": "Zoom:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Ошибка при удалении истории отмен. Это",
"About": "О проекте",
"Active": "Активный",
"Aden": "Aden",
"Advanced": "Продвинутый",
"All": "Все",
"Alpha": "Альфа",
"Alpha:": "Альфа:",
"Anonymous": "Анонимное",
"Anti aliasing": "Сглаживание",
"Application markup may have changed,": "Разметка приложения могла измениться,",
"Arial": "Arial",
"Arrow": "Стрелка",
"ArrowDown": "Стрелка вниз",
"ArrowLeft": "Стрелка влево",
"ArrowRight": "Стрелка вправо",
"ArrowUp": "Стрелка вверх",
"Author:": "Автор:",
"Auto Adjust Colors": "Автоматическая настройка цвета",
"Auto Kerning": "Автоматический кернинг",
"Average:": "В среднем:",
"Backspace": "Backspace",
"Base": "База",
"Basic": "Основа",
"Black and White": "Черный и Белый",
"Blue": "Синий",
"Blue channel:": "Синий канал:",
"Blueprint": "Чертеж",
"Blur Radius:": "Радиус Размытия:",
"Blur Tool": "Инструмент Размытия",
"Blur power:": "Сила размытия:",
"Borders": "Границы",
"Bottom": "Дно",
"Bottom to Top": "Снизу вверх",
"Bounds:": "Границы:",
"Box": "Коробка",
"Box Blur": "Размытие по рамке",
"Box blur": "Размытие по рамке",
"Brightness": "Яркость",
"Brightness:": "Яркость:",
"Bulge\/Pinch Tool": "Вдавливание\/вытяжение",
"Burn": "Выжигание",
"Can not animate 1 layer.": "Невозможно анимировать 1 слой.",
"Can not find previous layer.": "Не удается найти предыдущий слой.",
"Can not use this tool on current layer: image already takes all area.": "Невозможно использовать этот инструмент на текущем слое: изображение уже занимает всю область.",
"Cancel": "Отмена",
"Canvas Size": "Размер холста",
"Center": "Центр",
"Center x:": "Центр x:",
"Center y:": "Центр y:",
"Center:": "Центр:",
"Change Composition": "Изменить состав",
"Change Layer Details": "Изменить сведения о слое",
"Change Opacity": "Изменить непрозрачность",
"Channel:": "Источник:",
"Circle": "Круг",
"Clarendon": "Clarendon",
"Clear": "Очистить",
"Clear Selection": "Очистить Выбор",
"Clone Tool": "Инструмент клонирования",
"Clone count:": "Количество клонов:",
"Clone tool disabled for resized image. Please rasterize first.": "Инструмент клонирования отключен для изображения с измененным размером. Пожалуйста, сначала растрируйте.",
"Cloned edges": "Клонированные края",
"Close": "Закрывать",
"Color #": "Цвет #",
"Color Corrections": "Коррекция цвета",
"Color Palette": "Цветовая палитра",
"Color Zoom": "Усиление цвета",
"Color alpha value can not be zero.": "Значение цвета не может быть равно нулю.",
"Color to Alpha": "Цвет в прозрачность",
"Color zoom": "Усиление цвета",
"Color:": "Цвет:",
"Colors": "Цвета",
"Colors:": "Цвета:",
"Common Filters": "Обычные фильтры",
"Composition": "Состав",
"Composition:": "Состав:",
"Content Fill": "Заполнение содержимого",
"Contrast": "Контраст",
"Contrast:": "Контраст:",
"Convert layer to raster": "Растрировать слой",
"Convert to Raster": "Растрировать",
"Copy Selection": "Копировать выделение",
"Copy to Clipboard": "Скопировать в буфер",
"Courier": "Courier",
"Crop Tool": "Инструмент для Обрезки",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Обрезка повернутого слоя не поддерживается. Растрируйте его, чтобы продолжить.",
"Ctrl + C": "Ctrl + С",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "Ctrl+П",
"Ctrl+V": "Ctrl + V,",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "Выбранный",
"Current Color Preview": "Выбранный цвет",
"Custom": "Свой",
"Data URL": "URL данных",
"Data URL:": "URL данных:",
"Decrease": "Уменьшить",
"Decrease Color Depth": "Уменьшить глубину цвета",
"Degree:": "Градус:",
"Del": "Del",
"Delete": "Удалить",
"Delete Selection": "Удалить выделение",
"Denoise": "Шумоподавление",
"Desaturate Tool": "Инструмент обесцвечивания",
"Description:": "Описание:",
"Deutsch": "Deutsch",
"Differences": "Различия",
"Differences Down": "Разница вниз",
"Direction:": "Направление:",
"Dither": "Сгладить",
"Dithering:": "Сглаживание:",
"Dominant color:": "Доминирующий:",
"Dot Screen": "Точечный экран",
"Down": "Вниз",
"Duplicate": "Дублировать",
"Duplicate Layer": "Дублировать слой",
"Duplicate layer": "Дублировать слой",
"Dynamic": "Динамический",
"Edge": "Край",
"Edit": "Редактировать",
"Edit text...": "Редактировать текст...",
"Effect browser": "Подборка фильтров",
"Effects": "Фильтры",
"Effects browser": "Подборка фильтров",
"Email:": "Email:",
"Emboss": "Тиснение",
"Empty selection": "Пустой выбор",
"Empty selection or type not image.": "Пустой выбор или введите не изображение.",
"Enable autoresize:": "Автоматически увеличивать холст:",
"End": "Конец",
"English": "English",
"English (UK)": "английский (Великобритания)",
"Enrich": "Насытить",
"Enter": "Войти",
"Erase Tool": "Ластик",
"Erase on rotate object is disabled. Please rasterize first.": "Стирание при повороте объекта отключено. Пожалуйста, сначала растрируйте.",
"Error": "Ошибка",
"Error connecting to service.": "Ошибка подключения к сервису.",
"Error loading the list of fonts from Google.": "Ошибка загрузки списка шрифтов из Google.",
"Error registering service worker": "Ошибка регистрации сервис-воркера",
"Error: can not find filter:": "Ошибка: не удалось найти фильтр:",
"Error: can not find layer with id:": "Ошибка: не удается найти слой с идентификатором:",
"Error: missing details event target": "Ошибка: отсутствует цель события",
"Error: unknown layer type:": "Ошибка: неизвестный тип слоя:",
"Error: unsupported attribute type:": "Ошибка: неподдерживаемый тип атрибута:",
"Esc": "Esc",
"Escape": "ESC",
"Español": "Español",
"Expand edges": "Развернуть края",
"Exponent:": "Экспонент:",
"Export": "Экспорт",
"External": "Внешние инструменты",
"Factor:": "Фактор:",
"File": "Файл",
"File name:": "Имя файла:",
"File size:": "Размер файла:",
"Fill": "Заливка",
"Fill Tool": "Инструмент Заливки",
"Fit": "Вписать",
"Fit Window": "Вписать в окно",
"Fit window": "Вписать в окно",
"Flatten Image": "Свести изображение",
"Flip": "Отразить",
"FloydSteinberg-serpentine": "FloydSteinberg-serpentine",
"Font": "Шрифт",
"Français": "Français",
"Full HD, 1080p": "Full HD, 1080p",
"Full Screen": "Полноэкранный",
"Full layers data": "Данные полных слоев",
"Gap:": "Зазор:",
"Gaussian Blur": "Гауссовское Размытие",
"Gif delay:": "Задержка Gif:",
"Gingham": "Зонтик",
"GitHub:": "GitHub:",
"Gradient Radius:": "Радиус градиента:",
"Grains": "Зерна",
"Graphics Interchange Format": "Формат обмена графикой",
"Gray": "Серый",
"Grayscale": "Оттенки серого",
"Greek": "Греческий",
"Green": "Зеленый",
"Green channel:": "Зеленый канал:",
"Greyscale:": "Оттенки серого:",
"Grid": "Сетка",
"Grid on\/off": "Сетка вкл\/выкл",
"Guides": "Гайдлайны",
"Guides enabled.": "Гайдлайны включены",
"H Radius:": "H Радиус:",
"H. Align:": "H. Выравнивание:",
"Heatmap": "Тепловая карта",
"Height (%):": "Высота (%):",
"Height:": "Высота:",
"Help": "Помощь",
"Helvetica": "Helvetica",
"Hermite": "Hermite",
"Hex": "Hex",
"Hide": "Скрыть",
"Histogram": "Гистограмма",
"Histogram:": "Гистограмма:",
"Home": "Главная",
"Horizontal": "Горизонтально",
"Horizontal Alignment": "Горизонтальное выравнивание",
"Horizontal blur:": "Горизонтальное размытие:",
"Horizontal:": "Горизонтальный:",
"Hue": "Оттенок",
"Hue Rotate": "Вращение оттенка",
"Hue:": "Оттенок:",
"Image": "Изображение",
"Image data with multi-layers. Can be opened using miniPaint -": "Данные изображения с несколькими слоями. Может быть открыт с помощью miniPaint -",
"Impact": "Влиять",
"In proportion:": "Сохранять пропорции:",
"Increase": "Увеличить",
"Information": "Информация",
"Inkwell": "Inkwell",
"Insert": "Вставить",
"Insert guides": "Добавить гайд",
"Insert new layer": "Новый слой",
"Instagram Filters": "Фильтры Instagram",
"Invalid Hex Code": "Неверный HEX код",
"Italiano": "Italiano",
"JPG\/JPEG Format": "JPG\/JPEG Формат",
"Kerning:": "Интервал:",
"Key-Points": "Ключевые точки",
"KeyU": "КлючU",
"Keyboard Shortcuts": "Горячие клавиши",
"Keyword:": "Ключевое слово:",
"Lanczos": "Lanczos",
"Landscape": "Альбомная",
"Language": "Язык",
"Last modified": "Последнее изменение",
"Layer": "Слой",
"Layer details": "Активный слой",
"Layer is empty.": "Слой пуст.",
"Layer is not compatible with resize": "Слой несовместим с изменением размера",
"Layer is vector, convert it to raster to apply this tool.": "Слой является векторным, преобразуйте его в растровый, чтобы применить этот инструмент.",
"Layers": "Слои",
"Layers:": "Слои:",
"Layout:": "Ориентация:",
"Left": "Слева",
"Left to Right": "Слева направо",
"Level:": "Уровень:",
"Levels:": "Уровни:",
"Lietuvių": "Lietuvių",
"Lo-fi": "Lo-fi",
"Luminance:": "Освещенность:",
"Luminosity": "Освещенность",
"Magic Eraser Tool": "Волшебный ластик",
"Merge Down": "Соединить вниз",
"Merge Layers": "Соединить слои",
"Merged": "Объединенное",
"Metrics": "Метрики",
"Middle": "Средний",
"Missing at least 1 size parameter.": "Отсутствует хотя бы 1 параметр размера.",
"Missing permissions to write to Clipboard.cc": "Отсутствуют разрешения на запись в Clipboard.cc",
"Mode:": "Режим:",
"Module function not found.": "Функция модуля не найдена.",
"Modules class not found:": "Класс модулей не найден:",
"Monospace": "Моноширинный",
"Mosaic": "Мозаика",
"Mouse:": "Мышь:",
"Move": "Переместить",
"Move Layer": "Переместить слой",
"Move layer down": "Опустить слой ниже",
"Move layer up": "Поднять слой выше",
"Name:": "Имя:",
"Negative": "Негатив",
"New": "Новый",
"New Bezier Layer": "Новый слой Безье",
"New Brush Layer": "Новый слой Кисти",
"New Ellipse Layer": "Новый слой Эллипса",
"New File": "Новый файл",
"New Gradient Layer": "Новый слой Градиента",
"New Layer": "Новый слой",
"New Line Layer": "Новый слой Линии",
"New Pencil Layer": "Новый слой Карандаша",
"New Polygon Layer": "Новый полигональный слой",
"New Rectangle Layer": "Новый слой Прямоугольника",
"New Text Layer": "Новый Текстовый слой",
"New file": "Новый файл",
"New from Selection": "Новое из выделения",
"New layer": "Новый слой",
"Next": "Следующий",
"Night Vision": "Ночное видение",
"None": "Ничего",
"Nothing is selected.": "Ничего не выбрано.",
"Offset X:": "Смещение X:",
"Offset Y:": "Смещение Y:",
"Oil": "Масло",
"Ok": "ОК",
"Online image editor.": "Онлайн редактор изображений.",
"Opacity": "Непрозрачность",
"Opacity:": "Прозрачность:",
"Open": "Открыть",
"Open Data URL": "Открыть URL-адрес",
"Open Directory": "Открыть каталог",
"Open File": "Открыть файл",
"Open File Data URL": "URL-адрес файла",
"Open File URL": "Открыть URL-адрес",
"Open File Webcam": "Изображение с веб-камеры",
"Open Image": "Открыть файл",
"Open JSON File": "Открыть Файл JSON",
"Open Test Template": "Шаблон открытого теста",
"Open URL": "Открыть URL",
"Open data URL": "Открыть URL-адрес",
"Open from Webcam": "Изображение с веб-камеры",
"Original Size": "Оригинальный размер",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Конвертировать изображение в SVG",
"PageDown": "Листать вниз",
"PageUp": "Листать вверх",
"Palette": "Палитра",
"Parameter #1:": "Параметр #1:",
"Parameter #2:": "Параметр #2:",
"Paste": "Вставить",
"Pencil": "Карандаш",
"Percentage:": "Процент:",
"Pixels:": "Пиксели:",
"Placeholder comment for color channels": "Комментарий-заполнитель для цветовых каналов",
"Placeholder comment for color picker": "Комментарий-заполнитель для палитры цветов",
"Placeholder comment for color swatches": "Комментарий-заполнитель для образцов цвета",
"Portable Network Graphics": "Портативная Сетевая Графика",
"Portrait": "Портретная",
"Português": "Português",
"Position:": "Позиция:",
"Power:": "Сила:",
"Preview": "Навигация",
"Previous": "Предыдущий",
"Previous layer must be image, convert it to raster to apply this tool.": "Предыдущий слой должен быть изображением, растрируйте его, чтобы применить этот инструмент.",
"Print": "Распечатать",
"Quality:": "Качество:",
"Quick Load": "Быстрое открытие",
"Quick Save": "Быстрое сохранение",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Удалить Фон Изображения",
"Radial": "Радиальный",
"Radial gradient": "Радиальный градиент",
"Radius:": "Радиус:",
"Range:": "Диапазон:",
"Red": "Красный",
"Red channel:": "Красный канал:",
"Redo": "Повторить",
"Remove all": "Удалить все",
"Rename": "Переименовать",
"Rename Layer": "Переименовать слой",
"Rendered with errors.": "Отрисовано с ошибками.",
"Rendering...": "Отрисовка ...",
"Replace Color": "Заменить цвет",
"Replace color": "Заменить цвет",
"Replacement:": "Замена:",
"Report Issues": "Сообщить о проблемах",
"Reset": "Сброс",
"Resize": "Изменить размер",
"Resize Boundary": "Изменить размер границы",
"Resize Layer": "Изменить размер слоя",
"Resize Layers": "Изменить размер слоев",
"Resize Text Layer": "Изменить размер текстового слоя",
"Resized as background": "Изменено в качестве фона",
"Resized:": "Изменён размер:",
"Resolution:": "Разрешение:",
"Restore Alpha": "Восстановить прозрачность",
"Right": "Вправо",
"Right angle:": "Прямой угол:",
"Right to Left": "Справа налево",
"Rotate": "Повернуть",
"Rotate Layer": "Повернуть слой",
"Rotate is not supported on this type of object. Convert to raster?": "Поворот на этом типе объекта не поддерживается. Растрировать?",
"Rotate left": "Повернуть влево",
"Rotate:": "Поворот:",
"Ruler": "Линейки",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - Сжатие и сравнение изображений",
"Saturate": "Насытить",
"Saturation": "Насыщенность",
"Saturation:": "Насыщенность:",
"Save As": "Сохранить как",
"Save As Data URL": "Сохранить как base64",
"Save as": "Сохранить как",
"Save as type:": "Сохранить как тип:",
"Save layers:": "Сохранить слои:",
"Scaling up is not supported in Hermite, using Lanczos.": "В Hermite с использованием Lanczos масштабирование не поддерживается.",
"Scroll down": "Прокрутить вниз",
"Scroll up": "Прокрутка вверх",
"Search": "Поиск",
"Search Images": "Поиск изображений",
"Search for Font": "Поиск шрифта",
"Search:": "Поиск:",
"Select All": "Выбрать все",
"Select Text Layer": "Выбрать текстовый слой",
"Select object tool": "Выбор объекта",
"Selected": "Выбранный",
"Selection Tool": "Инструмент выделения",
"Sensitivity:": "Чувствительность:",
"Separated": "Отдельно",
"Separated (original types)": "Отдельно (оригинальный формат)",
"Sepia": "Сепия",
"Set Image Size": "Установить размер изображения",
"Settings": "Настройки",
"Shadow": "Тень",
"Shapes": "Фигуры",
"Shapes (H)": "Фигуры (H)",
"Sharpen": "Резкость",
"Sharpen Tool": "Инструмент резкости",
"Sharpen:": "Повысить четкость:",
"Shift + S": "Шифт + С",
"Shortcut Key:": "Быстрая клавиша:",
"Show": "Показывать",
"Show \/ Hide": "Показать \/ Спрятать",
"Show file size:": "Считать размер:",
"Simple": "Простой",
"Size is too big, max": "Размер слишком большой, максимум",
"Size:": "Размер:",
"Skip - layer must be image.": "Пропуск - слой должен быть изображением.",
"Solarize": "Высветлить",
"Sorry, cold not load getUserMedia() data:": "К сожалению, не удалось загрузить данные getUserMedia():",
"Sorry, image could not be loaded.": "К сожалению, изображение не может быть загружено.",
"Sorry, image could not be loaded. Try copy image and paste it.": "К сожалению, изображение не может быть загружено. Попробуйте скопировать изображение и вставьте его.",
"Sorry, image is too big, max 5 MB.": "К сожалению, изображение слишком большое, максимум 5 МБ.",
"Source coordinates saved.": "Исходные координаты сохранены.",
"Source is empty, right click on image or use long press to save source position.": "Источник пуст, щелкните изображение правой кнопкой мыши или нажмите и удерживайте, чтобы сохранить исходное положение.",
"Sprites": "Спрайты",
"Square": "Квадрат",
"Stream:": "Поток:",
"Strength:": "Прочность:",
"Strict": "Строго",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - Сжатие PNG и JPEG",
"Tab": "Вкладка",
"Tag Image File Format": "Формат файла изображения тега",
"Tahoma": "Tahoma",
"Target:": "Цель:",
"The quick brown fox jumps over the lazy dog.": "Быстрая коричневая лиса прыгает через ленивую собаку.",
"There": "Там",
"There are no layers behind.": "Позади нет слоев.",
"There is only 1 layer.": "Есть только 1 слой.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Слой должен содержать изображение, растрируйте его, чтобы применить этот инструмент.",
"Tilt Shift": "Tilt Shift",
"Times New Roman": "Times New Roman",
"Toaster": "Toaster",
"Toggle": " ",
"Toggle Color Channels": "Цветовые каналы",
"Toggle Color Picker": "Цветовая палитра",
"Toggle Menu": "Меню",
"Toggle Swatches": "Коллекция цветов",
"Tools": "Инструменты",
"Top": "Вверх",
"Top to Bottom": "Сверху вниз",
"Total pixels:": "Всего пикселей:",
"Translate": "Сдвинуть",
"Translate Layer": "Сдвинуть слой",
"Translate error, can not find dictionary:": "Ошибка перевода, не удалось найти словарь:",
"Transparent:": "Прозрачность:",
"Trim": "Обрезать",
"Trim Layers": "Обрезать слои",
"Trim borders:": "Обрезать границы:",
"Trim layer:": "Обрезной слой:",
"Trim white color?": "Обрезать белый цвет?",
"Type:": "Тип:",
"Türkçe": "Türkçe",
"Undo": "Отменить",
"Unique colors:": "Уникальные цвета:",
"Up": "Вверх",
"Update": "Обновить",
"Update Brush Layer": "Обновить слой Кисти",
"Update Pencil Layer": "Обновить слой карандаша",
"Update guides": "Обновить руководства",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Используйте комбинацию клавиш Ctrl + V для вставки из буфера обмена.",
"V Radius:": "В радиус:",
"V. Align:": "В. Выровнять:",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "Версия:",
"Vertical": "Вертикально",
"Vertical Alignment": "Вертикальное Выравнивание",
"Vertical blur:": "Вертикальное размытие:",
"Vertical:": "Вертикальное:",
"Vibrance": "Вибрация",
"View": "Вид",
"Vignette": "Виньетка",
"ViliusL": "ViliusL",
"Vintage": "Винтаж",
"Webcam": "Веб-камера",
"Webcam #": "Веб-камера #",
"Website:": "Веб-сайт:",
"Weppy File Format": "Формат файла Weppy",
"Width (%):": "Ширина (%):",
"Width:": "Ширина:",
"Windows Bitmap": "Растровое изображение Windows",
"Word": "Слово",
"Word + Letter": "Слово + Буква",
"Wrap At:": "Обернуть в:",
"Wrap:": "Обвернуть:",
"Wrong dimensions": "Неправильные размеры",
"Wrong file type, must be image or json.": "Неверный тип файла, тип файла должен быть изображением или json.",
"X end:": "X конец:",
"X position:": "X позиция:",
"X start:": "X начало:",
"X-Pro II": "X-Pro II",
"Y end:": "Y конец:",
"Y position:": "Y позиция:",
"Y start:": "Y начало:",
"You can also drag and drop items into browser.": "Вы также можете перетаскивать элементы в браузер.",
"Your browser does not support canvas or JavaScript is not enabled.": "Ваш браузер не поддерживает холст или JavaScript не включен.",
"Your browser does not support this format.": "Ваш браузер не поддерживает этот формат.",
"Your search did not match any images.": "Ваш поиск не соответствовал изображениям.",
"Zoom": "Приблизить",
"Zoom Blur": "Размытие Приближения",
"Zoom In": "Приблизить",
"Zoom Out": "Отдалить",
"Zoom blur": "Масштабирование",
"Zoom in": "Приблизить",
"Zoom out": "Отдалить",
"Zoom:": "Приблизить:"
}
+513
View File
@@ -0,0 +1,513 @@
{
"A problem occurred while removing undo history. It": "Geri alma geçmişini kaldırırken bir sorun oluştu. O",
"About": "hakkında",
"Active": "Aktif",
"Aden": "Aden",
"Advanced": "ileri",
"All": "Herşey",
"Alpha": "Alfa",
"Alpha:": "Alfa:",
"Anonymous": "Anonim",
"Anti aliasing": "Örtüşme önleme",
"Application markup may have changed,": "Uygulama işaretlemesi değişmiş olabilir,",
"Arial": "Arial",
"Arrow": "Ok",
"ArrowDown": "Aşağı ok",
"ArrowLeft": "ArrowLeft",
"ArrowRight": "ArrowRight",
"ArrowUp": "Yukarı ok",
"Author:": "Yazar:",
"Auto Adjust Colors": "Renkleri otomatik ayarla",
"Auto Kerning": "Otomatik Karakter Aralığı",
"Average:": "Ortalama:",
"Backspace": "Geri tuşu",
"Base": "baz",
"Basic": "Temel",
"Black and White": "Siyah ve beyaz",
"Blue": "Mavi",
"Blue channel:": "Mavi kanal:",
"Blueprint": "Taslak",
"Blur Radius:": "Bulanıklaştırma Yarıçapı:",
"Blur Tool": "Bulanıklık aracı",
"Blur power:": "Blur gücü:",
"Borders": "Sınırlar",
"Bottom": "Alt",
"Bottom to Top": "Alttan Üste",
"Bounds:": "Sınırlar:",
"Box": "Kutu",
"Box Blur": "Kutu bulanıklığı",
"Box blur": "Kutu bulanıklığı",
"Brightness": "Parlaklık",
"Brightness:": "Parlaklık:",
"Bulge\/Pinch Tool": "Bulge \/ Kıstırma Aracı",
"Burn": "Yanmak",
"Can not animate 1 layer.": "1 katmana canlandırma yapılamıyor.",
"Can not find previous layer.": "Önceki katmanı bulamıyorum.",
"Can not use this tool on current layer: image already takes all area.": "Bu araç geçerli katmanda kullanılamıyor: görüntü zaten tüm alanı kaplıyor.",
"Cancel": "İptal etmek",
"Canvas Size": "Tuval Boyutu",
"Center": "merkez",
"Center x:": "Merkez x:",
"Center y:": "Merkez y:",
"Center:": "merkez:",
"Change Composition": "Kompozisyonu Değiştir",
"Change Layer Details": "Katman Ayrıntılarını Değiştir",
"Change Opacity": "Opaklığı Değiştir",
"Channel:": "Kanal:",
"Circle": "Daire",
"Clarendon": "Clarendon",
"Clear": "Açık",
"Clear Selection": "Seçimi Temizle",
"Clone Tool": "Klonlama Aracı",
"Clone count:": "Klon sayısı:",
"Clone tool disabled for resized image. Please rasterize first.": "Yeniden boyutlandırılan resim için klonlama aracı devre dışı bırakıldı. Lütfen önce rasterleştirin.",
"Cloned edges": "Klonlanmış kenarlar",
"Close": "Kapalı",
"Color #": "Renk #",
"Color Corrections": "Renk düzeltmeleri",
"Color Palette": "Renk paleti",
"Color Zoom": "Renkli Zoom",
"Color alpha value can not be zero.": "Renkli alfa değeri sıfır olamaz.",
"Color to Alpha": "Alfanın renkleri",
"Color zoom": "Renkli yakınlaştırma",
"Color:": "Renk:",
"Colors": "Renkler",
"Colors:": "Renkler:",
"Common Filters": "Ortak Filtreler",
"Composition": "bileştirme, kompozisyon",
"Composition:": "Bileştirme, kompozisyon:",
"Content Fill": "İçerik doldurma",
"Contrast": "Kontrast",
"Contrast:": "Kontrast:",
"Convert layer to raster": "Katmanı raster'a dönüştür",
"Convert to Raster": "Rastera dönüştürün",
"Copy Selection": "Seçimi kopyala",
"Copy to Clipboard": "Panoya kopyala",
"Courier": "Kurye",
"Crop Tool": "Kırpma aracı",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "Döndürülmüş katmanda kırpma desteklenmez. Devam etmek için raster'e dönüştürün.",
"Ctrl + C": "Ctrl + C",
"Ctrl+A": "Ctrl + A",
"Ctrl+C": "Ctrl + C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl + V",
"Ctrl+Y": "Ctrl + Y",
"Ctrl+Z": "Ctrl + Z",
"Current": "şimdiki",
"Current Color Preview": "Mevcut Renk Önizlemesi",
"Custom": "görenek",
"Data URL": "Veri URL'si",
"Data URL:": "Veri URL'si:",
"Decrease": "Azaltmak",
"Decrease Color Depth": "Renk derinliğini azalt",
"Degree:": "Derece:",
"Del": "Del",
"Delete": "silmek",
"Delete Selection": "Seçimi sil",
"Denoise": "Denoise",
"Desaturate Tool": "Doygunluğu Azaltma Aracı",
"Description:": "Açıklama:",
"Deutsch": "Deutsch",
"Differences": "farklılıklar",
"Differences Down": "Farklar Aşağı",
"Direction:": "Yön:",
"Dither": "titreme",
"Dithering:": "taklidi:",
"Dominant color:": "Hakim renk:",
"Dot Screen": "Nokta Ekranı",
"Down": "Aşağı",
"Duplicate": "Çift",
"Duplicate Layer": "Yinelenen katman",
"Duplicate layer": "Yinelenen katman",
"Dynamic": "Dinamik",
"Edge": "kenar",
"Edit": "Düzenle",
"Edit text...": "Metni düzenle...",
"Effect browser": "Efekt tarayıcısı",
"Effects": "Etkileri",
"Effects browser": "Efekt tarayıcısı",
"Email:": "E-posta:",
"Emboss": "kabartma yapmak",
"Empty selection": "Boş seçim",
"Empty selection or type not image.": "Boş seçim veya resim değil yazın.",
"Enable autoresize:": "Otomatik yeniden boyutlandırmayı etkinleştir:",
"End": "Son",
"English": "ingilizce",
"English (UK)": "İngilizce (İngiltere)",
"Enrich": "Zenginleştirmek",
"Enter": "Giriş",
"Erase Tool": "Silme Aracı",
"Erase on rotate object is disabled. Please rasterize first.": "Nesneyi döndürürken silme devre dışı bırakılır. Lütfen önce rasterleştirin.",
"Error": "Hata",
"Error connecting to service.": "Hizmete bağlanırken hata oluştu.",
"Error loading the list of fonts from Google.": "Google'dan yazı tipi listesi yüklenirken hata oluştu.",
"Error registering service worker": "Hizmet çalışanı kaydedilirken hata oluştu",
"Error: can not find filter:": "Hata: filtre bulunamıyor:",
"Error: can not find layer with id:": "Hata: kimliğine sahip katman bulunamıyor:",
"Error: missing details event target": "Hata: eksik olan ayrıntılar etkinlik hedefi",
"Error: unknown layer type:": "Hata: bilinmeyen katman türü:",
"Error: unsupported attribute type:": "Hata: desteklenmeyen özellik türü:",
"Esc": "ESC",
"Escape": "Kaçış",
"Español": "Español",
"Expand edges": "Kenarları genişlet",
"Exponent:": "Üs:",
"Export": "İhracat",
"External": "Harici",
"Factor:": "Faktör:",
"File": "Dosya",
"File name:": "Dosya adı:",
"File size:": "Dosya boyutu:",
"Fill": "doldurmak",
"Fill Tool": "Doldurma Aracı",
"Fit": "Fit",
"Fit Window": "Pencereye sığdır",
"Fit window": "Pencereyi sığdır",
"Flatten Image": "Resmi Düzleştir",
"Flip": "fiske",
"FloydSteinberg-serpentine": "FloydSteinberg-serpantin",
"Font": "Yazı tipi",
"Français": "Français",
"Full HD, 1080p": "Tam HD, 1080p",
"Full Screen": "Tam ekran",
"Full layers data": "Tam katman verileri",
"Gap:": "boşluk:",
"Gaussian Blur": "Gauss Bulanıklığı",
"Gif delay:": "Gif gecikmesi:",
"Gingham": "Şemsiye",
"GitHub:": "GitHub:",
"Gradient Radius:": "Gradyan Yarıçapı:",
"Grains": "Taneler",
"Graphics Interchange Format": "Grafik Değişim Biçimi",
"Gray": "Gri",
"Grayscale": "Gri tonlama",
"Greek": "Yunan",
"Green": "Yeşil",
"Green channel:": "Yeşil kanal:",
"Greyscale:": "Gri tonlama:",
"Grid": "Kafes",
"Grid on\/off": "Izgara açık \/ kapalı",
"Guides": "Kılavuzlar",
"Guides enabled.": "Kılavuzlar etkinleştirildi.",
"H Radius:": "H Radius:",
"H. Align:": "H. Hizala:",
"Heatmap": "Sıcaklık haritası",
"Height (%):": "Yükseklik (%):",
"Height:": "Yükseklik:",
"Help": "yardım et",
"Helvetica": "Helvetica",
"Hermite": "Hermite",
"Hex": "Hex",
"Hide": "Saklamak",
"Histogram": "Histogram",
"Histogram:": "Histogram:",
"Home": "Ev",
"Horizontal": "Yatay",
"Horizontal Alignment": "Yatay hizalama",
"Horizontal blur:": "Yatay bulanıklık:",
"Horizontal:": "Yatay:",
"Hue": "Ton",
"Hue Rotate": "Ton Döndür",
"Hue:": "Ton:",
"Image": "görüntü",
"Image data with multi-layers. Can be opened using miniPaint -": "Çok katmanlı görüntü verileri. MiniPaint ile açılabilir -",
"Impact": "darbe",
"In proportion:": "Orantılı olarak:",
"Increase": "Artırmak",
"Information": "Bilgi",
"Inkwell": "Inkwell",
"Insert": "Sokmak",
"Insert guides": "Kılavuzları ekle",
"Insert new layer": "Yeni katman ekle",
"Instagram Filters": "Instagram Filtreleri",
"Invalid Hex Code": "Geçersiz Hex Kodu",
"Italiano": "Italiano",
"JPG\/JPEG Format": "JPG \/ JPEG Biçimi",
"Kerning:": "Karakter aralığı:",
"Key-Points": "Anahtar noktaları",
"KeyU": "KeyU",
"Keyboard Shortcuts": "Klavye kısayolları",
"Keyword:": "Anahtar kelime:",
"Lanczos": "Lanczos",
"Landscape": "Manzara",
"Language": "Dil",
"Last modified": "Son düzenleme",
"Layer": "Katman",
"Layer details": "Katman ayrıntıları",
"Layer is empty.": "Katman boş.",
"Layer is not compatible with resize": "Katman yeniden boyutlandırmayla uyumlu değil",
"Layer is vector, convert it to raster to apply this tool.": "Katman vektördür, bu aracı uygulamak için onu raster'e dönüştürün.",
"Layers": "Katmanlar",
"Layers:": "Katmanlar:",
"Layout:": "Düzen:",
"Left": "Ayrıldı",
"Left to Right": "Soldan sağa",
"Level:": "Seviye:",
"Levels:": "Seviyeleri:",
"Lietuvių": "Litvanya",
"Lo-fi": "Lo-fi",
"Luminance:": "Parlaklık:",
"Luminosity": "Parlaklık",
"Magic Eraser Tool": "Sihirli Silgi Aracı",
"Merge Down": "Aşağı Birleştir",
"Merge Layers": "Katmanları birleştirmek",
"Merged": "Birleştirilmiş",
"Metrics": "Metrikler",
"Middle": "Orta",
"Missing at least 1 size parameter.": "En az 1 boyut parametresi eksik.",
"Missing permissions to write to Clipboard.cc": "Clipboard.cc'ye yazma izinleri eksik",
"Mode:": "Mod:",
"Module function not found.": "Modül işlevi bulunamadı.",
"Modules class not found:": "Modüller sınıf bulunamadı:",
"Monospace": "Tek aralıklı",
"Mosaic": "Mozaik",
"Mouse:": "Fare:",
"Move": "Hareket",
"Move Layer": "Katmanı Taşı",
"Move layer down": "Katmanı aşağı taşı",
"Move layer up": "Katmanı yukarı taşı",
"Name:": "Adı:",
"Negative": "Negatif",
"New": "Yeni",
"New Bezier Layer": "Yeni Bezier Katmanı",
"New Brush Layer": "Yeni Fırça Katmanı",
"New Ellipse Layer": "Yeni Elips Katmanı",
"New File": "Yeni dosya",
"New Gradient Layer": "Yeni Gradyan Katmanı",
"New Layer": "Yeni tabaka",
"New Line Layer": "Yeni Çizgi Katmanı",
"New Pencil Layer": "Yeni Kalem Katmanı",
"New Polygon Layer": "Yeni Çokgen Katmanı",
"New Rectangle Layer": "Yeni Dikdörtgen Katman",
"New Text Layer": "Yeni Metin Katmanı",
"New file": "Yeni dosya",
"New from Selection": "Seçimden yeni",
"New layer": "Yeni katman",
"Next": "Sonraki",
"Night Vision": "Gece görüşü",
"None": "Yok",
"Nothing is selected.": "Hiçbir şey seçilmedi.",
"Offset X:": "Ofset X:",
"Offset Y:": "Ofset Y:",
"Oil": "Sıvı yağ",
"Ok": "Tamam",
"Online image editor.": "Çevrimiçi görüntü düzenleyici.",
"Opacity": "opaklık",
"Opacity:": "Saydamlık:",
"Open": "Açık",
"Open Data URL": "Açık Veri URL'si",
"Open Directory": "Açık sözlük",
"Open File": "Açık dosya",
"Open File Data URL": "Dosya Verileri URL'sini Aç",
"Open File URL": "Dosya URL'sini Aç",
"Open File Webcam": "Dosya Web Kamerasını Aç",
"Open Image": "Resmi Aç",
"Open JSON File": "JSON Dosyasını Aç",
"Open Test Template": "Test Şablonunu Aç",
"Open URL": "Link aç",
"Open data URL": "Açık veri URL'si",
"Open from Webcam": "Web Kamerasından Aç",
"Original Size": "Orijinal boyut",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - Resmi SVG'ye Dönüştür",
"PageDown": "PageDown",
"PageUp": "Sayfa yukarı",
"Palette": "palet",
"Parameter #1:": "Parametre # 1:",
"Parameter #2:": "Parametre # 2:",
"Paste": "Yapıştırmak",
"Pencil": "Kalem",
"Percentage:": "Yüzde:",
"Pixels:": "Piksel:",
"Placeholder comment for color channels": "Renk kanalları için yer tutucu yorumu",
"Placeholder comment for color picker": "Renk seçici için yer tutucu yorumu",
"Placeholder comment for color swatches": "Renk örnekleri için yer tutucu yorumu",
"Portable Network Graphics": "taşınabilir Ağ Grafikleri",
"Portrait": "Vesika",
"Português": "Português",
"Position:": "Konum:",
"Power:": "Güç:",
"Preview": "Ön izleme",
"Previous": "Önceki",
"Previous layer must be image, convert it to raster to apply this tool.": "Önceki katman resim olmalıdır, bu aracı uygulamak için raster haline getirin.",
"Print": "baskı",
"Quality:": "Kalite:",
"Quick Load": "Hızlı yükleme",
"Quick Save": "Hızlı kaydet",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - Resim Arka Planını Kaldır",
"Radial": "Radyal",
"Radial gradient": "Radyal degrade",
"Radius:": "radius:",
"Range:": "aralık:",
"Red": "Kırmızı",
"Red channel:": "Kırmızı kanal:",
"Redo": "Yeniden yap",
"Remove all": "Hepsini kaldır",
"Rename": "Adını değiştirmek",
"Rename Layer": "Katmanı Yeniden Adlandır",
"Rendered with errors.": "Hatalarla oluşturuldu.",
"Rendering...": "Oluşturuluyor ...",
"Replace Color": "Renk Değiştir",
"Replace color": "Rengi değiştir",
"Replacement:": "Değiştirme:",
"Report Issues": "Sorunları bildir",
"Reset": "Reset",
"Resize": "yeniden boyutlandırma",
"Resize Boundary": "Sınırı Yeniden Boyutlandır",
"Resize Layer": "Katmanı Yeniden Boyutlandır",
"Resize Layers": "Katmanları Yeniden Boyutlandır",
"Resize Text Layer": "Metin Katmanını Yeniden Boyutlandır",
"Resized as background": "Arka plan olarak yeniden boyutlandırıldı",
"Resized:": "Yeniden boyutlandırıldı:",
"Resolution:": "Çözüm:",
"Restore Alpha": "Alfa geri yükle",
"Right": "Sağ",
"Right angle:": "Doğru açı:",
"Right to Left": "Sağdan sola",
"Rotate": "Döndürme",
"Rotate Layer": "Katmanı Döndür",
"Rotate is not supported on this type of object. Convert to raster?": "Döndürme, bu tür nesne üzerinde desteklenmiyor. Rastere dönüştürün?",
"Rotate left": "Sola dön",
"Rotate:": "Dönüşümlü:",
"Ruler": "Cetvel",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - Resimleri Sıkıştır ve Karşılaştır",
"Saturate": "bombalamak",
"Saturation": "Doyma",
"Saturation:": "Doyma:",
"Save As": "Farklı kaydet",
"Save As Data URL": "Veri URL'si olarak kaydet",
"Save as": "Farklı kaydet",
"Save as type:": "Türünü kaydet:",
"Save layers:": "Katmanları kaydet:",
"Scaling up is not supported in Hermite, using Lanczos.": "Lanczos kullanılarak Hermite'de ölçek büyütme desteklenmez.",
"Scroll down": "Aşağı kaydır",
"Scroll up": "Yukarı kaydırmak",
"Search": "Arama",
"Search Images": "Resimleri ara",
"Search for Font": "Yazı Tipi Ara",
"Search:": "Aramak:",
"Select All": "Hepsini seç",
"Select Text Layer": "Metin Katmanı Seçin",
"Select object tool": "Nesne aracını seçin",
"Selected": "seçilmiş",
"Selection Tool": "Seçim aracı",
"Sensitivity:": "Duyarlılık:",
"Separated": "Ayrılmış",
"Separated (original types)": "Ayrılmış (orijinal türler)",
"Sepia": "Sepya",
"Set Image Size": "Görüntü Boyutunu Ayarla",
"Settings": "Ayarlar",
"Shadow": "Gölge",
"Shapes": "Şekiller",
"Shapes (H)": "Şekiller (H)",
"Sharpen": "keskinleştirmek",
"Sharpen Tool": "Aleti keskinleştir",
"Sharpen:": "keskinleştir:",
"Shift + S": "Üst Karakter + S",
"Shortcut Key:": "Kısayol tuşu:",
"Show": "Göstermek",
"Show \/ Hide": "Göster \/ gizle",
"Show file size:": "Dosya boyutunu göster:",
"Simple": "Basit",
"Size is too big, max": "Boyut çok büyük, maks.",
"Size:": "Boyut:",
"Skip - layer must be image.": "Atlama - katman resim olmalıdır.",
"Solarize": "güneşte bırakmak",
"Sorry, cold not load getUserMedia() data:": "Maalesef getUserMedia () verilerini yükleme değil:",
"Sorry, image could not be loaded.": "Maalesef resim yüklenemedi.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Üzgünüz, resim yüklenemedi. Resmi kopyala ve yapıştırmayı deneyin.",
"Sorry, image is too big, max 5 MB.": "Maalesef, resim çok büyük, maksimum 5 MB.",
"Source coordinates saved.": "Kaynak koordinatlar kaydedildi.",
"Source is empty, right click on image or use long press to save source position.": "Kaynak boş, görüntüye sağ tıklayın veya kaynak konumunu kaydetmek için uzun basın.",
"Sprites": "Spritelar",
"Square": "Kare",
"Stream:": "Akış:",
"Strength:": "Sertlik:",
"Strict": "sıkı",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - PNG ve JPEG'i sıkıştır",
"Tab": "Sekme",
"Tag Image File Format": "Etiket Görüntüsü Dosya Formatı",
"Tahoma": "Tahoma",
"Target:": "Hedef:",
"The quick brown fox jumps over the lazy dog.": "Hızlı kahverengi tilki tembel köpeğin üzerinden atlıyor.",
"There": "Orada",
"There are no layers behind.": "Arkada hiçbir katman yok.",
"There is only 1 layer.": "Sadece bir tabaka var.",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Katman görüntü olmalı, onu uygulamak için rastgele dönüştürmelidir.",
"Tilt Shift": "Eğim Kaydırma",
"Times New Roman": "Times New Roman",
"Toaster": "Tost makinası",
"Toggle": "geçiş",
"Toggle Color Channels": "Renk Kanallarını Değiştir",
"Toggle Color Picker": "Renk Seçiciyi Değiştir",
"Toggle Menu": "Menüyü Değiştir",
"Toggle Swatches": "Renk Örneklerini Aç \/ Kapat",
"Tools": "Araçlar",
"Top": "Üst",
"Top to Bottom": "Yukarıdan Aşağıya",
"Total pixels:": "Toplam piksel:",
"Translate": "Çevirmek",
"Translate Layer": "Katmanı Çevir",
"Translate error, can not find dictionary:": "Çeviri hatası, sözlük bulunamadı:",
"Transparent:": "Şeffaf:",
"Trim": "düzeltmek",
"Trim Layers": "Katmanları Kırp",
"Trim borders:": "Kenarlıkları kırp:",
"Trim layer:": "Döşeme tabakası:",
"Trim white color?": "Beyaz rengini keser misin?",
"Type:": "Tip:",
"Türkçe": "Türkçe",
"Undo": "Geri alma",
"Unique colors:": "Eşsiz renkler:",
"Up": "yukarı",
"Update": "Güncelleme",
"Update Brush Layer": "Fırça Katmanını Güncelle",
"Update Pencil Layer": "Kalem Katmanını Güncelle",
"Update guides": "Kılavuzları güncelleyin",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "Pano'dan yapıştırmak için Ctrl + V klavye kısayolunu kullanın.",
"V Radius:": "V Yarıçapı:",
"V. Align:": "V. Hizala:",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "Versiyon:",
"Vertical": "Dikey",
"Vertical Alignment": "Dikey hizalama",
"Vertical blur:": "Dikey bulanıklık:",
"Vertical:": "Dikey:",
"Vibrance": "Titreşim",
"View": "Görüş",
"Vignette": "skeç",
"ViliusL": "ViliusL",
"Vintage": "bağbozumu",
"Webcam": "Web kamerası",
"Webcam #": "Web kamerası #",
"Website:": "Web sitesi:",
"Weppy File Format": "Weppy Dosya Biçimi",
"Width (%):": "Genişlik (%):",
"Width:": "Genişlik:",
"Windows Bitmap": "Windows Bit Eşlem",
"Word": "Kelime",
"Word + Letter": "Kelime + Harf",
"Wrap At:": "Şuraya Sar:",
"Wrap:": "Paketlemek:",
"Wrong dimensions": "Yanlış boyutlar",
"Wrong file type, must be image or json.": "Yanlış dosya türü, resim veya json olmalı.",
"X end:": "X sonu:",
"X position:": "X konumu:",
"X start:": "X start:",
"X-Pro II": "X-Pro II",
"Y end:": "Sonum:",
"Y position:": "Y pozisyonu:",
"Y start:": "Y başlatın:",
"You can also drag and drop items into browser.": "Ayrıca öğeleri tarayıcıya sürükleyip bırakabilirsiniz.",
"Your browser does not support canvas or JavaScript is not enabled.": "Tarayıcınız tuvali desteklemiyor veya JavaScript etkin değil.",
"Your browser does not support this format.": "Tarayıcınız bu biçimi desteklemiyor.",
"Your search did not match any images.": "Aramanız herhangi bir resimle eşleşmedi.",
"Zoom": "yakınlaştırma",
"Zoom Blur": "Zum Bulanıklığı",
"Zoom In": "Yakınlaştır",
"Zoom Out": "Uzaklaştır",
"Zoom blur": "Yakınlaştırma bulanıklığı",
"Zoom in": "Yakınlaştır",
"Zoom out": "Uzaklaştır",
"Zoom:": "zum:"
}
+507
View File
@@ -0,0 +1,507 @@
{
"24-Points star": "",
"A problem occurred while removing undo history. It": "Sorry, a problem occurred while removing the undo history.",
"About": "",
"Active": "",
"Add Borders": "",
"Aden": "",
"Advanced": "",
"All": "",
"Alpha": "",
"Alpha:": "",
"Anonymous": "",
"Anti aliasing": "",
"Application markup may have changed,": "The application markup may have changed",
"Arial": "",
"Arrow": "",
"ArrowDown": "",
"ArrowLeft": "",
"ArrowRight": "",
"ArrowUp": "",
"Author:": "",
"Auto Adjust Colors": "Auto Adjust the Colours",
"Auto Kerning": "",
"Average:": "",
"Backspace": "",
"Base": "",
"Basic": "",
"Black and White": "",
"Blue": "",
"Blue channel:": "",
"Blueprint": "",
"Blur Radius:": "",
"Blur Tool": "",
"Blur power:": "",
"Borders": "",
"Bottom": "",
"Bottom to Top": "",
"Bounds:": "",
"Box": "",
"Box Blur": "",
"Box blur": "",
"Brightness": "",
"Brightness:": "",
"Bulge\/Pinch Tool": "",
"Burn": "",
"Can not animate 1 layer.": "Sorry, you can not animate just 1 layer, you need at least 2 layers.",
"Can not find previous layer.": "Sorry, I can not find the previous layer.",
"Cancel": "",
"Canvas Size": "",
"Canvas size": "",
"Center": "Centre",
"Center x:": "Centre x:",
"Center y:": "Centre y:",
"Center:": "Centre:",
"Change Composition": "",
"Change Layer Details": "",
"Change Opacity": "",
"Channel:": "",
"Circle": "",
"Clarendon": "",
"Clear": "",
"Clear Selection": "",
"Clone Tool": "",
"Clone count:": "",
"Clone tool disabled for resized image. Sorry.": "Sorry, the clone tool is disabled for use on a resized asset (image). Undo the resize, clone the asset (image) then resize it again.",
"Cloned edges": "",
"Color #": "Colour #",
"Color Corrections": "Colour Corrections",
"Color Palette": "Colour Palette",
"Color Zoom": "Colour Zoom",
"Color alpha value can not be zero.": "The colour alpha value can not be zero. Please change it.",
"Color to Alpha": "Colour to Alpha",
"Color zoom": "Colour zoom",
"Color:": "Colour:",
"Colors": "Colours",
"Colors:": "Colours:",
"Common Filters": "",
"Composition": "",
"Composition:": "",
"Content Fill": "",
"Contrast": "",
"Contrast:": "",
"Convert to Raster": "Convert to a Raster",
"Copy Selection": "",
"Copy to Clipboard": "",
"Copy:": "",
"Courier": "",
"Crop Tool": "",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "You can not crop a rotated layer. Please convert it to a raster to continue.",
"Ctrl+A": "",
"Ctrl+C": "",
"Ctrl+V": "",
"Ctrl+Y": "",
"Ctrl+Z": "",
"Ctrl-P": "",
"Current": "",
"Current Color Preview": "Current Colour Preview",
"Custom": "",
"Data URL": "",
"Data URL:": "",
"Decrease": "",
"Decrease Color Depth": "Decrease Colour Depth",
"Degree:": "",
"Del": "",
"Delete": "",
"Delete Selection": "",
"Denoise": "",
"Desaturate Tool": "",
"Description:": "",
"Deutsch": "",
"Differences": "",
"Differences Down": "",
"Direction:": "",
"Dither": "",
"Dithering:": "",
"Dominant color:": "Dominant colour:",
"Dot Screen": "",
"Down": "",
"Duplicate": "",
"Duplicate Layer": "",
"Dynamic": "",
"Edge": "",
"Edit": "",
"Edit text...": "",
"Effect browser": "",
"Effects": "",
"Effects browser": "",
"Email:": "",
"Emboss": "",
"Empty selection": "",
"Empty selection or type not image.": "You have selected nothing or the asset is not an image.",
"Enable guides:": "",
"Enable snap:": "",
"End": "",
"English": "English UK",
"Enrich": "",
"Enter": "",
"Erase Tool": "",
"Erase on rotate object is disabled. Sorry.": "Sorry you can not erase on rotated asset (object). Remove the rotation then you can delete it.",
"Error": "",
"Error connecting to service.": "",
"Error loading the list of fonts from Google.": "There is an error loading the list of fonts from Google. Please report this.",
"Error registering service worker": "",
"Error: can not find filter:": "",
"Error: can not find layer with id:": "",
"Error: missing details event target": "",
"Error: unknown layer type:": "",
"Esc": "",
"Escape": "",
"Español": "",
"Exit confirmation:": "",
"Expand edges": "",
"Exponent:": "",
"Export": "",
"External": "",
"Factor:": "",
"File": "",
"File name:": "",
"File size:": "",
"Fill": "",
"Fill Tool": "",
"Fit": "",
"Fit Window": "",
"Flatten Image": "",
"Flip": "",
"FloydSteinberg-serpentine": "",
"Font": "",
"Français": "",
"Full HD, 1080p": "",
"Full Screen": "",
"Full layers data": "",
"Gap:": "",
"Gaussian Blur": "",
"Gif delay:": "",
"Gingham": "",
"GitHub:": "",
"Gradient Radius:": "",
"Grains": "",
"Graphics Interchange Format": "",
"Gray": "",
"Grayscale": "",
"Greek": "",
"Green": "",
"Green channel:": "",
"Greyscale:": "",
"Grid": "",
"Grid on\/off": "",
"Guides": "",
"Guides enabled.": "",
"H Radius:": "",
"H. Align:": "",
"Heatmap": "",
"Height (%):": "",
"Height:": "",
"Help": "",
"Helvetica": "",
"Hermite": "",
"Hex": "",
"Histogram": "",
"Histogram:": "",
"Home": "",
"Horizontal": "",
"Horizontal Alignment": "",
"Horizontal blur:": "",
"Horizontal:": "",
"Hue": "",
"Hue Rotate": "",
"Hue:": "",
"Image": "",
"Image data with multi-layers. Can be opened using miniPaint -": "You can open asset (image) data with multi-layers using miniPaint. -",
"Impact": "",
"Increase": "",
"Information": "",
"Inkwell": "",
"Insert": "",
"Insert guides": "",
"Insert:": "",
"Instagram Filters": "",
"Invalid Hex Code": "",
"Italiano": "",
"JPG\/JPEG Format": "",
"Kerning:": "",
"Key-Points": "",
"KeyU": "",
"Keyboard Shortcuts": "",
"Keyword:": "",
"Lanczos": "",
"Language": "",
"Last modified": "",
"Layer": "",
"Layer details": "",
"Layer is not compatible with resize": "Sorry, this layer is not compatible with resize",
"Layer is vector, convert it to raster to apply this tool.": "Sorry this layer is a vector, please convert it to a raster to apply this tool. (Layer, Convert to a Raster)",
"Layers": "",
"Layers:": "",
"Left": "",
"Left to Right": "",
"Level:": "",
"Levels:": "",
"Lietuvių": "",
"Lo-fi": "",
"Luminance:": "",
"Luminosity": "",
"Magic Eraser Tool": "",
"Merge Down": "",
"Merge Layers": "",
"Merged": "",
"Metrics": "",
"Middle": "",
"Missing at least 1 size parameter.": "Sorry, you are missing at least 1 size parameter.",
"Missing permissions to write to Clipboard.cc": "",
"Mode:": "",
"Module function not found.": "",
"Modules class not found:": "",
"Monospace": "",
"Mosaic": "",
"Mouse:": "",
"Move": "",
"Move Layer": "",
"Move down": "",
"Move up": "",
"Name:": "",
"Needs at least 2 layers.": "You need at least 2 layers. Please make another layer using Layer, New or dragging a new asset (image) into the browser.",
"Negative": "",
"New": "",
"New Brush Layer": "",
"New Ellipse Layer": "",
"New File": "",
"New Gradient Layer": "",
"New Layer": "",
"New Line Layer": "",
"New Pencil Layer": "",
"New Rectangle Layer": "",
"New Text Layer": "",
"New file": "",
"New from Selection": "",
"New layer": "",
"New width can not be smaller then current width": "You can not make the new width smaller then current width.",
"Night Vision": "",
"None": "",
"Nothing is selected.": "Sorry, you have not selected anything, please try again.",
"Offset X:": "",
"Offset Y:": "",
"Oil": "",
"Ok": "",
"Online image editor.": "",
"Opacity": "",
"Opacity:": "",
"Open": "",
"Open Data URL": "",
"Open Directory": "",
"Open File": "",
"Open File Data URL": "",
"Open File URL": "",
"Open File Webcam": "",
"Open Image": "",
"Open JSON File": "",
"Open Test Template": "",
"Open URL": "",
"Open data URL": "",
"Open from Webcam": "",
"Original Size": "",
"PNGTOSVG - Convert Image to SVG": "",
"PageDown": "",
"PageUp": "",
"Palette": "",
"Parameter #1:": "",
"Parameter #2:": "",
"Paste": "",
"Pencil": "",
"Percentage:": "",
"Pixels:": "",
"Placeholder comment for color channels": "Placeholder comment for colour channels",
"Placeholder comment for color picker": "Placeholder comment for colour picker",
"Placeholder comment for color swatches": "Placeholder comment for colour swatches",
"Portable Network Graphics": "",
"Português": "",
"Position:": "",
"Power:": "",
"Preview": "",
"Previous": "",
"Previous layer must be image, convert it to raster to apply this tool.": "The previous layer must be an asset (image), please convert it to a raster to apply this tool.",
"Print": "",
"Quality:": "",
"Quick Load": "",
"Quick Save": "",
"REMOVE.BG - Remove Image Background": "",
"Radial": "",
"Radial gradient": "",
"Radius:": "",
"Range:": "",
"Red": "",
"Red channel:": "",
"Redo": "",
"Remove all": "",
"Rename": "",
"Rename Layer": "",
"Rendered with errors.": "",
"Rendering...": "",
"Replace Color": "Replace Colour",
"Replace color": "Replace colour",
"Replacement:": "",
"Report Issues": "",
"Reset": "",
"Resize": "",
"Resize Boundary": "",
"Resize Layer": "",
"Resize Layers": "",
"Resize Text Layer": "",
"Resized as background": "",
"Resized:": "",
"Resolution:": "",
"Restore Alpha": "",
"Right": "",
"Right angle:": "",
"Right to Left": "",
"Rotate": "",
"Rotate Layer": "",
"Rotate is not supported on this type of object. Convert to raster?": "Sorry, rotate is not supported on this type of asset (object), would you like to convert it to a raster?",
"Rotate left": "",
"Rotate:": "",
"Ruler": "",
"SQUOOSH - Compress and Compare Images": "",
"Safe search:": "",
"Saturate": "",
"Saturation": "",
"Saturation:": "",
"Save (Export)": "",
"Save As": "",
"Save As Data URL": "",
"Save as": "",
"Save as type:": "",
"Save layers:": "",
"Scaling up is not supported in Hermite, using Lanczos.": "",
"Scroll down": "",
"Scroll up": "",
"Search": "",
"Search Images": "",
"Search for Font": "",
"Select All": "",
"Select Text Layer": "",
"Select object tool": "",
"Selected": "",
"Selection Tool": "",
"Sensitivity:": "",
"Separated": "",
"Separated (original types)": "",
"Sepia": "",
"Set Image Size": "",
"Settings": "",
"Shadow": "",
"Shadow:": "",
"Shapes": "",
"Sharpen": "",
"Sharpen Tool": "",
"Sharpen:": "",
"Shortcut Key:": "",
"Show \/ Hide": "",
"Show file size:": "",
"Simple": "",
"Size is too big, max": "",
"Size:": "",
"Skip - layer must be image.": "Skip - layer must be an asset (image).",
"Solarize": "",
"Sorry, cold not load getUserMedia() data:": "Sorry, I could not load getUserMedia() data:",
"Sorry, image could not be loaded.": "Sorry, the asset (image) could not be loaded.",
"Sorry, image could not be loaded. Try copy image and paste it.": "Sorry, the asset (image) could not be loaded. Try copying the image and pasting it.",
"Sorry, image is too big, max 5 MB.": "Sorry, the asset (image) is too big, max size is 5 MB.",
"Source coordinates saved.": "",
"Source is empty, right click on image or use long press to save source position.": "Sorry, the source is empty, right click on the asset (image) or use a long press to save source position.",
"Sprites": "",
"Square": "",
"Stream:": "",
"Strength:": "",
"Strict": "",
"TINYPNG - Compress PNG and JPEG": "",
"Tab": "",
"Tag Image File Format": "",
"Tahoma": "",
"Target:": "",
"The quick brown fox jumps over the lazy dog.": "",
"Theme": "",
"There": "",
"There are no layers behind.": "",
"There is only 1 layer.": "",
"Thick guides:": "",
"This layer must contain an image. Please convert it to raster to apply this tool.": "Sorry, this layer must contain an asset (image). Please convert it to a raster to apply this tool.",
"Tilt Shift": "",
"Times New Roman": "",
"Toaster": "",
"Toggle": "",
"Toggle Color Channels": "Toggle Colour Channels",
"Toggle Color Picker": "Toggle Colour Picker",
"Toggle Menu": "",
"Toggle Swatches": "",
"Tools": "",
"Top": "",
"Top to Bottom": "",
"Total pixels:": "",
"Translate": "",
"Translate Layer": "",
"Translate error, can not find dictionary:": "Translate error, I can not find the dictionary:",
"Transparency background:": "",
"Transparent:": "",
"Trim": "",
"Trim Layers": "",
"Trim borders:": "",
"Trim layer:": "",
"Trim white color?": "Trim white colour?",
"Type:": "",
"Türkçe": "",
"Undo": "",
"Unique colors:": "Unique colours:",
"Units": "",
"Up": "",
"Update": "",
"Update Brush Layer": "",
"Update Pencil Layer": "",
"Update guides": "",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "You can use Ctrl+V on the keyboard shortcut to paste from the Clipboard.",
"V Radius:": "",
"V. Align:": "",
"Valencia": "",
"Verdana": "",
"Version:": "",
"Vertical": "",
"Vertical Alignment": "",
"Vertical blur:": "",
"Vertical:": "",
"Vibrance": "",
"View": "",
"Vignette": "",
"ViliusL": "",
"Vintage": "",
"Webcam": "",
"Webcam #": "",
"Website:": "",
"Weppy File Format": "",
"Width (%):": "",
"Width:": "",
"Windows Bitmap": "",
"Word": "",
"Word + Letter": "",
"Wrap At:": "",
"Wrap:": "",
"Wrong dimensions": "",
"Wrong file type, must be image or json.": "This is the wrong file type, it must be an asset (image) or json.",
"X end:": "",
"X position:": "",
"X start:": "",
"X-Pro II": "",
"Y end:": "",
"Y position:": "",
"Y start:": "",
"You can also drag and drop items into browser.": "You can also drag and drop assets (items) into browser.",
"Your browser does not support canvas or JavaScript is not enabled.": "",
"Your browser does not support this format.": "",
"Your search did not match any images.": "Your search did not match any assets (images).",
"Zoom": "",
"Zoom Blur": "",
"Zoom In": "",
"Zoom Out": "",
"Zoom blur": "",
"Zoom in": "",
"Zoom out": "",
"Zoom:": ""
}
+535
View File
@@ -0,0 +1,535 @@
{
"A problem occurred while removing undo history. It": "删除撤销历史记录时发生问题。它",
"About": "关于",
"Active": "活动",
"Aden": "Aden",
"Advanced": "高级",
"All": "全部",
"Alpha": "透明度",
"Alpha:": "透明度:",
"Animation": "动画",
"Anonymous": "匿名",
"Anti aliasing": "抗锯齿",
"Application markup may have changed,": "应用标记可能已更改,",
"Arial": "Arial",
"Arrow": "箭头",
"ArrowDown": "向下箭头",
"ArrowLeft": "向左箭头",
"ArrowRight": "向右箭头",
"ArrowUp": "向上箭头",
"Author:": "作者:",
"Auto Adjust Colors": "自动调整颜色",
"Auto Kerning": "自动紧排",
"Auto select": "自动选择",
"Average:": "平均值:",
"Backspace": "退格键",
"Base": "基础",
"Basic": "基本",
"Black and White": "黑白",
"Blue": "蓝色",
"Blue channel:": "蓝色通道:",
"Blueprint": "蓝图",
"Blur": "模糊工具",
"Blur Radius:": "模糊半径:",
"Blur Tool": "模糊工具",
"Blur power:": "模糊强度:",
"Borders": "边框",
"Bottom": "底部",
"Bottom to Top": "从底部到顶部",
"Bounds:": "边界:",
"Box": "方框",
"Box Blur": "方框模糊",
"Box blur": "方框模糊",
"Brightness": "亮度",
"Brightness:": "亮度:",
"Brush": "刷子工具",
"Bulge": "凸出",
"Bulge/Pinch Tool": "凸出/收缩工具",
"Burn": "加深",
"Can not animate 1 layer.": "无法对1个图层进行动画。",
"Can not find previous layer.": "找不到上一个图层。",
"Can not use this tool on current layer: image already takes all area.": "无法在当前图层上使用此工具:图像已覆盖整个区域。",
"Cancel": "取消",
"Canvas Size": "画布尺寸",
"Center": "中心",
"Center x:": "中心x",
"Center y:": "中心y",
"Center:": "中心:",
"Change Composition": "更改合成",
"Change Layer Details": "更改图层详情",
"Change Opacity": "更改不透明度",
"Channel:": "通道:",
"Circle": "圆圈",
"Clarendon": "Clarendon",
"Clear": "清除",
"Clear Selection": "清除选区",
"Clone": "克隆工具",
"Clone Tool": "克隆工具",
"Clone count:": "克隆数量:",
"Clone tool disabled for resized image. Please rasterize first.": "对已调整大小的图像禁用克隆工具。请先栅格化。",
"Cloned edges": "克隆边缘",
"Close": "关闭",
"Color 1:": "颜色 1",
"Color 2:": "颜色 2",
"Color #": "颜色 #",
"Color Corrections": "颜色校正",
"Color Palette": "颜色调色板",
"Color Zoom": "颜色缩放",
"Color alpha value can not be zero.": "颜色的 alpha 值不能为零。",
"Color to Alpha": "颜色转换为透明",
"Color zoom": "颜色缩放",
"Color:": "颜色:",
"Colors": "颜色",
"Colors:": "颜色:",
"Common Filters": "常见滤镜",
"Composition": "合成",
"Composition:": "合成:",
"Content Fill": "内容填充",
"Contiguous": "连续",
"Contrast": "对比度",
"Contrast:": "对比度:",
"Convert layer to raster": "将图层转换为栅格图",
"Convert to Raster": "转换为栅格图",
"Copy Selection": "复制选区",
"Copy to Clipboard": "复制到剪贴板",
"Courier": "Courier",
"Crop": "裁剪工具",
"Crop Tool": "裁剪工具",
"Crop on rotated layer is not supported. Convert it to raster to continue.": "不支持旋转图层上的裁剪。请将其转换为位图以继续。",
"Ctrl + C": "Ctrl + C",
"Ctrl+A": "Ctrl+A",
"Ctrl+C": "Ctrl+C",
"Ctrl+P": "Ctrl+P",
"Ctrl+V": "Ctrl+V",
"Ctrl+Y": "Ctrl+Y",
"Ctrl+Z": "Ctrl+Z",
"Current": "当前",
"Current Color Preview": "当前颜色预览",
"Custom": "自定义",
"Data URL": "数据 URL",
"Data URL:": "数据 URL",
"Decrease": "减少",
"Decrease Color Depth": "减少色彩深度",
"Degree:": "角度:",
"Del": "删除",
"Delay:": "延迟:",
"Delete": "删除",
"Delete Selection": "删除选区",
"Denoise": "降噪",
"Desaturate Tool": "去色工具",
"Description:": "描述:",
"Deutsch": "德语",
"Differences": "差异",
"Differences Down": "差异缩小",
"Direction:": "方向:",
"Dither": "抖动",
"Dithering:": "抖动:",
"Dominant color:": "主色调:",
"Dot Screen": "点阵",
"Down": "向下",
"Duplicate": "复制",
"Duplicate Layer": "复制图层",
"Duplicate layer": "复制图层",
"Dynamic": "动态",
"Edge": "边缘",
"Edit": "编辑",
"Edit text...": "编辑文本...",
"Effect browser": "特效浏览器",
"Effects": "特效",
"Effects browser": "特效浏览器",
"Email:": "邮箱:",
"Emboss": "浮雕",
"Empty selection": "空选区",
"Empty selection or type not image.": "空选区或未输入图像。",
"Enable autoresize:": "启用自动调整大小:",
"End": "结束",
"English": "英语",
"English (UK)": "英语(英国)",
"Enrich": "增强",
"Enter": "输入",
"Erase Tool": "橡皮擦工具",
"Erase on rotate object is disabled. Please rasterize first.": "禁用旋转对象上的橡皮擦。请先栅格化。",
"Error": "错误",
"Error connecting to service.": "连接到服务时出错。",
"Error loading the list of fonts from Google.": "加载 Google 字体列表时出错。",
"Error registering service worker": "注册服务工作者时出错",
"Error: can not find filter:": "错误:无法找到滤镜:",
"Error: can not find layer with id:": "错误:无法找到带有 ID 的图层:",
"Error: missing details event target": "错误:缺少详细信息事件目标",
"Error: unknown layer type:": "错误:未知的图层类型:",
"Error: unsupported attribute type:": "错误:不支持的属性类型:",
"Esc": "退出",
"Escape": "逃脱",
"Español": "西班牙语",
"Expand edges": "扩展边缘",
"Exponent:": "指数:",
"Export": "导出",
"External": "外部",
"Erase": "橡皮擦工具",
"Factor:": "因子:",
"File": "文件",
"File name:": "文件名:",
"File size:": "文件大小:",
"Fill": "填充",
"Fill:": "填充:",
"Fill Tool": "填充工具",
"Fit": "适应",
"Fit Window": "适应窗口",
"Fit window": "适应窗口",
"Flatten Image": "图像拉平",
"Flip": "翻转",
"FloydSteinberg-serpentine": "FloydSteinberg-蛇形",
"Font": "字体",
"Font:": "字体:",
"Français": "法语",
"Full HD, 1080p": " 全高清,1080p",
"Full Screen": "全屏",
"Full layers data": "全层数据",
"Gap:": "间距:",
"Gaussian Blur": "高斯模糊",
"Gif delay:": "动图延迟:",
"Gingham": "方格",
"GitHub:": "GitHub",
"Gradient": "渐变工具",
"Gradient Radius:": "渐变半径:",
"Grains": "颗粒",
"Graphics Interchange Format": "图形交换格式",
"Gray": "灰色",
"Grayscale": "灰度",
"Greek": "希腊语",
"Green": "绿色",
"Green channel:": "绿色通道:",
"Greyscale:": "灰度:",
"Grid": "网格",
"Grid on/off": "打开/关闭网格",
"Guides": "参考线",
"Guides enabled.": "参考线已启用。",
"H Radius:": "水平半径:",
"H. Align:": "水平对齐:",
"Heatmap": "热力图",
"Height (%):": "高度(%):",
"Height:": "高度:",
"Help": "帮助",
"Helvetica": "黑体",
"Hermite": "Hermite",
"Hex": "十六进制",
"Hide": "隐藏",
"Histogram": "直方图",
"Histogram:": "直方图:",
"Home": "主页",
"Horizontal": "水平",
"Horizontal Alignment": "水平对齐",
"Horizontal blur:": "水平模糊:",
"Horizontal:": "水平:",
"Hue": "色调",
"Hue Rotate": "色调旋转",
"Hue:": "色调:",
"Image": "图片",
"Image data with multi-layers. Can be opened using miniPaint -": "图像数据带有多层。可使用miniPaint打开 -",
"Impact": "影响",
"In proportion:": "按比例:",
"Increase": "增加",
"Information": "信息",
"Inkwell": "墨井",
"Insert": "插入",
"Insert guides": "插入参考线",
"Insert new layer": "插入新图层",
"Instagram Filters": "Instagram 滤镜",
"Invalid Hex Code": "无效的十六进制代码",
"Italiano": "意大利语",
"JPG/JPEG Format": "JPG / JPEG格式",
"Kerning:": "字距:",
"Key-Points": "关键点",
"KeyU": "键 U",
"Keyboard Shortcuts": "键盘快捷键",
"Keyword:": "关键字:",
"Lanczos": "Lanczos",
"Landscape": "横向",
"Language": "语言",
"Last modified": "上次修改",
"Layer": "图层",
"Layer details": "图层详情",
"Layer is empty.": "图层为空。",
"Layer is not compatible with resize": "图层不兼容调整大小",
"Layer is vector, convert it to raster to apply this tool.": "图层为矢量,转换为栅格以应用此工具。",
"Layers": "图层",
"Layers:": "图层:",
"Layout:": "布局:",
"Leading:": "行距:",
"Left": "左",
"Left to Right": "左到右",
"Level:": "层级:",
"Levels:": "层级:",
"Lietuvių": "立陶宛语",
"Lo-fi": "低保真",
"Luminance:": "亮度:",
"Luminosity": "亮度",
"Magic Eraser Tool": "魔术橡皮擦工具",
"Merge Down": "向下合并",
"Merge Layers": "合并图层",
"Merged": "已合并",
"Metrics": "指标",
"Middle": "居中",
"Missing at least 1 size parameter.": "至少缺少1个尺寸参数。",
"Missing permissions to write to Clipboard.cc": "缺少写入Clipboard.cc的权限",
"Mode:": "模式:",
"Module function not found.": "未找到模块功能。",
"Modules class not found:": "未找到模块类:",
"Monospace": "等宽字体",
"Mosaic": "马赛克",
"Mouse:": "鼠标:",
"Move": "移动",
"Move Layer": "移动图层",
"Move layer down": "向下移动图层",
"Move layer up": "向上移动图层",
"Name:": "名称:",
"Negative": "负片",
"New": "新建",
"New Bezier Layer": "新贝塞尔曲线图层",
"New Brush Layer": "新画笔图层",
"New Ellipse Layer": "新椭圆图层",
"New File": "新建文件",
"New Gradient Layer": "新渐变图层",
"New Layer": "新建图层",
"New Line Layer": "新线条图层",
"New Pencil Layer": "新铅笔图层",
"New Polygon Layer": "新多边形图层",
"New Rectangle Layer": "新矩形图层",
"New Text Layer": "新文本图层",
"New file": "新建文件",
"New from Selection": "从选择新建",
"New layer": "新建图层",
"Next": "下一个",
"Night Vision": "夜视",
"None": "无",
"Nothing is selected.": "未选择任何内容。",
"Offset X:": "X偏移:",
"Offset Y:": "Y偏移:",
"Oil": "油画",
"Ok": "确定",
"Online image editor.": "在线图像编辑器。",
"Opacity": "不透明度",
"Opacity:": "不透明度:",
"Open": "打开",
"Open Data URL": "打开数据URL",
"Open Directory": "打开目录",
"Open File": "打开文件",
"Open File Data URL": "打开数据URL文件",
"Open File URL": "打开文件网址",
"Open File Webcam": "打开网络摄像头文件",
"Open Image": "打开图像",
"Open JSON File": "打开JSON文件",
"Open Test Template": "打开测试模板",
"Open URL": "打开网址",
"Open data URL": "打开数据URL",
"Open from Webcam": "从摄像头打开",
"Original Size": "原始大小",
"PNGTOSVG - Convert Image to SVG": "PNGTOSVG - 将图像转换为SVG格式",
"PageDown": "下一页",
"PageUp": "上一页",
"Palette": "调色板",
"Parameter #1:": "参数1",
"Parameter #2:": "参数2",
"Paste": "粘贴",
"Pencil": "铅笔工具",
"Percentage:": "百分比:",
"Pick color": "吸管工具",
"Pixels:": "像素:",
"Placeholder comment for color channels": "颜色通道的占位符注释",
"Placeholder comment for color picker": "颜色选择器的占位符注释",
"Placeholder comment for color swatches": "颜色样本的占位符注释",
"Play": "播放",
"Portable Network Graphics": "便携式网络图形",
"Portrait": "纵向",
"Português": "葡萄牙语",
"Position:": "位置:",
"Power:": "功率:",
"Preview": "预览",
"Previous": "上一个",
"Previous layer must be image, convert it to raster to apply this tool.": "前一图层必须为图像,将其转换为栅格以应用此工具。",
"Print": "打印",
"Quality:": "质量:",
"Quick Load": "快速加载",
"Quick Save": "快速保存",
"REMOVE.BG - Remove Image Background": "REMOVE.BG - 移除图像背景",
"Radial": "径向",
"Radial gradient": "径向渐变",
"Radius:": "半径:",
"Range:": "范围:",
"Red": "红色",
"Red channel:": "红色通道:",
"Redo": "重做",
"Remove all": "移除全部",
"Rename": "重命名",
"Rename Layer": "重命名图层",
"Rendered with errors.": "渲染出现错误。",
"Rendering...": "渲染中...",
"Replace Color": "替换颜色",
"Replace color": "替换颜色",
"Replacement:": "替换:",
"Report Issues": "报告问题",
"Reset": "重置",
"Resize": "调整大小",
"Resize Boundary": "调整边界",
"Resize Layer": "调整图层大小",
"Resize Layers": "调整图层大小",
"Resize Text Layer": "调整文本图层大小",
"Resized as background": "调整为背景",
"Resized:": "调整大小:",
"Resolution:": "分辨率:",
"Restore Alpha": "恢复透明度",
"Right": "右",
"Right angle:": "直角:",
"Right to Left": "从右到左",
"Rotate": "旋转",
"Rotate Layer": "旋转图层",
"Rotate is not supported on this type of object. Convert to raster?": "此类型对象不支持旋转。转换为栅格图?",
"Rotate left": "向左旋转",
"Rotate:": "旋转:",
"Ruler": "标尺",
"SQUOOSH - Compress and Compare Images": "SQUOOSH - 压缩和比较图像",
"Saturate": "饱和度",
"Saturation": "饱和度",
"Saturation:": "饱和度:",
"Save As": "另存为",
"Save As Data URL": "另存为数据URL",
"Save as": "另存为",
"Save as type:": "另存为类型:",
"Save layers:": "保存图层:",
"Scaling up is not supported in Hermite, using Lanczos.": "Hermite不支持放大,请使用Lanczos。",
"Scroll down": "向下滚动",
"Scroll up": "向上滚动",
"Search": "搜索",
"Search Images": "搜索图像",
"Search for Font": "搜索字体",
"Search:": "搜索:",
"Select All": "全选",
"Select Text Layer": "选择文本图层",
"Select object tool": "选择对象工具",
"Selected": "已选择",
"Selection": "选择工具",
"Selection Tool": "选择工具",
"Sensitivity:": "灵敏度:",
"Separated": "分离",
"Separated (original types)": "分离(原始类型)",
"Sepia": "棕褐色",
"Set Image Size": "设置图像尺寸",
"Settings": "设置",
"Shadow": "阴影",
"Shapes": "形状",
"Shapes (H)": "形状 (H)",
"Sharpen": "锐化",
"Sharpen Tool": "锐化工具",
"Sharpen:": "锐化:",
"Shift + S": "Shift + S",
"Shortcut Key:": "快捷键:",
"Show": "显示",
"Show \/ Hide": "显示 \/ 隐藏",
"Show file size:": "显示文件大小:",
"Simple": "简单",
"Size is too big, max": "尺寸太大,最大值为",
"Size:": "尺寸:",
"Skip - layer must be image.": "跳过 - 图层必须是图像。",
"Solarize": "曝光反转",
"Sorry, cold not load getUserMedia() data:": "抱歉,无法加载 getUserMedia() 数据:",
"Sorry, image could not be loaded.": "抱歉,无法加载图片。",
"Sorry, image could not be loaded. Try copy image and paste it.": "抱歉,图片无法加载。尝试复制图像并粘贴。",
"Sorry, image is too big, max 5 MB.": "抱歉,图片太大,最大值为5 MB。",
"Source coordinates saved.": "源坐标已保存。",
"Source is empty, right click on image or use long press to save source position.": "源为空,右键单击图像或长按保存源位置。",
"Source layer:": "源图层:",
"Sprites": "图像精灵",
"Square": "正方形",
"Stream:": "流:",
"Strength:": "强度:",
"Strict": "严格",
"Stroke size:": "线条粗细:",
"TINYPNG - Compress PNG and JPEG": "TINYPNG - 压缩PNG和JPEG",
"Tab": "标签",
"Tag Image File Format": "标记图像文件格式",
"Tahoma": "Tahoma",
"Target:": "目标:",
"The quick brown fox jumps over the lazy dog.": "敏捷的棕色狐狸跳过了懒狗。",
"There": "那里",
"There are no layers behind.": "背后没有图层。",
"There is only 1 layer.": "只有1个图层。",
"This layer must contain an image. Please convert it to raster to apply this tool.": "此图层必须包含图像。请将其转换为光栅以应用此工具。",
"Tilt Shift": "视角移位",
"Times New Roman": "Times New Roman",
"Toaster": "Toaster",
"Toggle": "切换",
"Toggle Color Channels": "切换颜色通道",
"Toggle Color Picker": "切换颜色选择器",
"Toggle Menu": "切换菜单",
"Toggle Swatches": "切换样本",
"Tools": "工具",
"Top": "顶部",
"Top to Bottom": "从上到下",
"Total pixels:": "总像素数:",
"Translate": "翻译",
"Translate Layer": "翻译图层",
"Translate error, can not find dictionary:": "翻译错误,找不到字典:",
"Transparent:": "透明:",
"Trim": "裁剪",
"Trim Layers": "裁剪图层",
"Trim borders:": "裁剪边框:",
"Trim layer:": "裁剪图层:",
"Trim white color?": "裁剪白色吗?",
"Text": " 文本工具",
"Type:": "类型:",
"Türkçe": "土耳其语",
"Undo": "撤销",
"Unique colors:": "唯一颜色:",
"Up": "向上",
"Update": "更新",
"Update Brush Layer": "更新画笔图层",
"Update Pencil Layer": "更新铅笔图层",
"Update guides": "更新指南",
"Use Ctrl+V keyboard shortcut to paste from Clipboard.": "使用 Ctrl+V 快捷键从剪贴板粘贴。",
"V Radius:": "垂直半径:",
"V. Align:": "垂直对齐:",
"Valencia": "Valencia",
"Verdana": "Verdana",
"Version:": "版本:",
"Vertical": "垂直",
"Vertical Alignment": "垂直对齐",
"Vertical blur:": "垂直模糊:",
"Vertical:": "垂直:",
"Vibrance": "饱和度",
"View": "视图",
"Vignette": "晕影",
"ViliusL": "ViliusL",
"Vintage": "复古",
"Webcam": "摄像头",
"Webcam #": "摄像头 #",
"Website:": "网站:",
"Weppy File Format": "Weppy文件格式",
"Width (%):": "宽度(%):",
"Width:": "宽度:",
"Windows Bitmap": "Windows位图",
"Word": "词",
"Word + Letter": "词 + 字母",
"Wrap At:": "在此处换行:",
"Wrap:": "自动换行:",
"Wrong dimensions": "尺寸错误",
"Wrong file type, must be image or json.": "文件类型错误,必须是图像或JSON。",
"X end:": "X 结束:",
"X position:": "X 位置:",
"X start:": "X 开始:",
"X-Pro II": "X-Pro II",
"Y end:": "Y 结束:",
"Y position:": "Y 位置:",
"Y start:": "Y 开始:",
"You can also drag and drop items into browser.": "您也可以将项目拖放到浏览器中。",
"Your browser does not support canvas or JavaScript is not enabled.": "您的浏览器不支持画布或JavaScript未启用。",
"Your browser does not support this format.": "您的浏览器不支持此格式。",
"Your search did not match any images.": "您的搜索未匹配任何图片。",
"Zoom": "缩放",
"Zoom Blur": "缩放模糊",
"Zoom In": "放大",
"Zoom Out": "缩小",
"Zoom blur": "缩放模糊",
"Zoom in": "放大",
"Zoom out": "缩小",
"Zoom:": "缩放:"
}
@@ -0,0 +1,309 @@
/*!
canvas-to-tiff version 1.0.0
By Epistemex (c) 2015-2016
www.epistemex.com
MIT License (this header required)
*/
/**
* Static helper object that can convert a CORS-compliant canvas element
* to a 32-bits TIFF file (buffer, Blob and data-URI). The TIFF is by
* default saved in big-endian format with interleaved RGBA data.
*
* @type {{toArrayBuffer: Function, toBlob: Function, toDataURL: Function}}
* @namespace
*/
var CanvasToTIFF = {
/**
* @private
*/
_dly: 9,
/**
* @private
*/
_error: null,
/**
* Add error handler (function) in case of any error
* @param fn
*/
setErrorHandler: function(fn) {
this._error = fn
},
/**
* Convert a canvas element to ArrayBuffer containing a TIFF file
* with support for alpha. The call is asynchronous
* so a callback must be provided.
*
* Note that CORS requirement must be fulfilled.
*
* @param {HTMLCanvasElement} canvas - the canvas element to convert
* @param {function} callback - called when conversion is done. Argument is ArrayBuffer
* @param {object} [options] - an option object
* @param {boolean} [options.littleEndian=false] - set to true to produce a little-endian based TIFF
* @param {number} [options.dpi=96] - DPI for both X and Y directions. Default 96 DPI (PPI).
* @param {number} [options.dpiX=96] - DPI for X directions (overrides options.dpi).
* @param {number} [options.dpiY=96] - DPI for Y directions (overrides options.dpi).
* @static
*/
toArrayBuffer: function(canvas, callback, options) {
options = options || {};
var me = this;
try {
var w = canvas.width,
h = canvas.height,
offset = 0,
iOffset = 258, // todo calc based on offset field length, add to final offset when compiled
//iOffsetPtr,
entries = 0,
offsetList = [],
idfOffset,
sid = "\x63\x61\x6e\x76\x61\x73\x2d\x74\x6f\x2d\x74\x69\x66\x66\x20\x30\x2e\x34\0",
lsb = !!options.littleEndian,
dpiX = +(options.dpiX || options.dpi || 96)|0,
dpiY = +(options.dpiY || options.dpi || 96)|0,
idata = canvas.getContext("2d").getImageData(0, 0, w, h),
length = idata.data.length,
fileLength = iOffset + length,
file = new ArrayBuffer(fileLength),
file8 = new Uint8Array(file),
view = new DataView(file),
pos = 0,
date = new Date(),
dateStr;
// Header
set16(lsb ? 0x4949 : 0x4d4d); // II or MM
set16(42); // magic 42
set32(8); // offset to first IFD
// IFD
addIDF(); // IDF start
addEntry(0xfe, 4, 1, 0); // NewSubfileType
addEntry(0x100, 4, 1, w); // ImageWidth
addEntry(0x101, 4, 1, h); // ImageLength (height)
addEntry(0x102, 3, 4, offset, 8); // BitsPerSample
addEntry(0x103, 3, 1, 1); // Compression
addEntry(0x106, 3, 1, 2); // PhotometricInterpretation: RGB
addEntry(0x111, 4, 1, iOffset, 0); // StripOffsets
addEntry(0x115, 3, 1, 4); // SamplesPerPixel
addEntry(0x117, 4, 1, length); // StripByteCounts
addEntry(0x11a, 5, 1, offset, 8); // XResolution
addEntry(0x11b, 5, 1, offset, 8); // YResolution
addEntry(0x128, 3, 1, 2); // ResolutionUnit: inch
addEntry(0x131, 2, sid.length, offset, getStrLen(sid)); // sid
addEntry(0x132, 2, 0x14, offset, 0x14); // Datetime
addEntry(0x152, 3, 1, 2); // ExtraSamples
endIDF();
// Fields section > long ---------------------------
// BitsPerSample (2x4), 8,8,8,8
set32(0x00080008);
set32(0x00080008);
// StripOffset to bitmap data
//set32(iOffset);
// StripByteCounts
//set32(length);
// XRes PPI
set32(dpiX);
set32(1);
// YRes PPI
set32(dpiY);
set32(1);
// sid
setStr(sid);
// date
dateStr = date.getFullYear() + ":" + pad2(date.getMonth() + 1) + ":" + pad2(date.getDate()) + " ";
dateStr += pad2(date.getHours()) + ":" + pad2(date.getMinutes()) + ":" + pad2(date.getSeconds());
setStr(dateStr);
// Image data here (todo if very large, split into block based copy)
file8.set(idata.data, iOffset);
// make actual async
setTimeout(function() { callback(file) }, me._dly);
}
catch(err) {
if (me._error) me._error(err.toString())
}
function pad2(str) {
str += "";
return str.length === 1 ? "0" + str : str
}
// helper method to move current buffer position
function set16(data) {
view.setUint16(pos, data, lsb);
pos += 2
}
function set32(data) {
view.setUint32(pos, data, lsb);
pos += 4
}
function setStr(str) {
var i = 0;
while(i < str.length) view.setUint8(pos++, str.charCodeAt(i++) & 0xff, lsb);
if (pos & 1) pos++
}
function getStrLen(str) {
var l = str.length;
return l & 1 ? l + 1 : l
}
function addEntry(tag, type, count, value, dltOffset) {
set16(tag);
set16(type);
set32(count);
if (dltOffset) {
//if (tag === 0x111) iOffsetPtr = pos;
//iOffset += dltOffset;
offset += dltOffset;
offsetList.push(pos);
}
if (count === 1 && type === 3 && !dltOffset) {
set16(value);
set16(0); // pad
}
else {
set32(value);
}
entries++
}
function addIDF(offset) {
idfOffset = offset || pos;
pos += 2;
}
function endIDF() {
view.setUint16(idfOffset, entries, lsb);
set32(0);
var delta = 14 + entries * 12; // 14 = offset to IDF (8) + IDF count (2) + end pointer (4)
// compile offsets
for(var i = 0, p, o; i < offsetList.length; i++) {
p = offsetList[i];
o = view.getUint32(p, lsb);
view.setUint32(p, o + delta, lsb);
}
//view.setUint32(iOffsetPtr, iOffset + delta, lsb);
}
},
/**
* Converts a canvas to TIFF file, returns a Blob representing the
* file. This can be used with URL.createObjectURL(). The call is
* asynchronous so a callback must be provided.
*
* Note that CORS requirement must be fulfilled.
*
* @param {HTMLCanvasElement} canvas - the canvas element to convert
* @param {function} callback - called when conversion is done. Argument is a Blob
* @param {object} [options] - an option object - see toArrayBuffer for details
* @static
*/
toBlob: function(canvas, callback, options) {
this.toArrayBuffer(canvas, function(file) {
callback(new Blob([file], {type: "image/tiff"}));
}, options || {});
},
/**
* Converts a canvas to TIFF file, returns an ObjectURL (for Blob)
* representing the file. The call is asynchronous so a callback
* must be provided.
*
* **Important**: To avoid memory-leakage you must revoke the returned
* ObjectURL when no longer needed:
*
* var _URL = self.URL || self.webkitURL || self;
* _URL.revokeObjectURL(url);
*
* Note that CORS requirement must be fulfilled.
*
* @param {HTMLCanvasElement} canvas - the canvas element to convert
* @param {function} callback - called when conversion is done. Argument is a Blob
* @param {object} [options] - an option object - see toArrayBuffer for details
* @static
*/
toObjectURL: function(canvas, callback, options) {
this.toBlob(canvas, function(blob) {
var url = self.URL || self.webkitURL || self;
callback(url.createObjectURL(blob))
}, options || {});
},
/**
* Converts the canvas to a data-URI representing a BMP file. The
* call is asynchronous so a callback must be provided.
*
* Note that CORS requirement must be fulfilled.
*
* @param {HTMLCanvasElement} canvas - the canvas element to convert
* @param {function} callback - called when conversion is done. Argument is an data-URI (string)
* @param {object} [options] - an option object - see toArrayBuffer for details
* @static
*/
toDataURL: function(canvas, callback, options) {
var me = this;
me.toArrayBuffer(canvas, function(file) {
var buffer = new Uint8Array(file),
blockSize = 1<<20,
block = blockSize,
bs = "", base64 = "", i = 0, l = buffer.length;
// This is a necessary step before we can use btoa. We can
// replace this later with a direct byte-buffer to Base-64 routine.
// Will do for now, impacts only with very large bitmaps (in which
// case toBlob should be used).
(function prepBase64() {
while(i < l && block-- > 0) bs += String.fromCharCode(buffer[i++]);
if (i < l) {
block = blockSize;
setTimeout(prepBase64, me._dly);
}
else {
// convert string to Base-64
i = 0;
l = bs.length;
block = 180000; // must be divisible by 3
(function toBase64() {
base64 += btoa(bs.substr(i, block));
i += block;
(i < l)
? setTimeout(toBase64, me._dly)
: callback("data:image/tiff;base64," + base64);
})();
}
})();
}, options || {});
}
};
export default CanvasToTIFF;
+152
View File
@@ -0,0 +1,152 @@
import Helper_class from './helpers.js';
/**
* image pasting into canvas
*
* @param {string} canvas_id - canvas id
* @param {boolean} autoresize - if canvas will be resized
*/
class Clipboard_class {
constructor(on_paste) {
var _self = this;
this.Helper = new Helper_class();
this.on_paste = on_paste;
this.ctrl_pressed = false;
this.command_pressed = false;
this.pasteCatcher;
this.paste_mode;
//handlers
document.addEventListener('keydown', function (e) {
_self.on_keyboard_action(e);
}, false); //firefox fix
document.addEventListener('keyup', function (e) {
_self.on_keyboardup_action(e);
}, false); //firefox fix
document.addEventListener('paste', function (e) {
_self.paste_auto(e);
}, false); //official paste handler
this.init();
}
//constructor - prepare
init() {
var _self = this;
//if using auto
if (window.Clipboard)
return true;
this.pasteCatcher = document.createElement("div");
this.pasteCatcher.setAttribute("id", "paste_ff");
this.pasteCatcher.setAttribute("contenteditable", "");
this.pasteCatcher.style.cssText = 'opacity:0;position:fixed;top:0px;left:0px;';
this.pasteCatcher.style.marginLeft = "-20px";
this.pasteCatcher.style.width = "10px";
document.body.appendChild(this.pasteCatcher);
// create an observer instance
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (this.paste_mode == 'auto' || this.ctrl_pressed == false || mutation.type != 'childList')
return true;
//if paste handle failed - capture pasted object manually
if (mutation.addedNodes.length == 1) {
if (mutation.addedNodes[0].src != undefined) {
//image
_self.paste_createImage(mutation.addedNodes[0].src);
}
//register cleanup after some time.
setTimeout(function () {
this.pasteCatcher.innerHTML = '';
}, 20);
}
});
});
var target = document.getElementById('paste_ff');
var config = {attributes: true, childList: true, characterData: true};
observer.observe(target, config);
}
//default paste action
paste_auto(e) {
if (this.Helper.is_input(e.target))
return;
this.paste_mode = '';
if (!window.Clipboard) {
this.pasteCatcher.innerHTML = '';
}
if (e.clipboardData) {
var items = e.clipboardData.items;
if (items) {
this.paste_mode = 'auto';
//access data directly
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
//image
var blob = items[i].getAsFile();
var URLObj = window.URL || window.webkitURL;
var source = URLObj.createObjectURL(blob);
this.paste_createImage(source);
}
}
e.preventDefault();
}
else {
//wait for DOMSubtreeModified event
//https://bugzilla.mozilla.org/show_bug.cgi?id=891247
}
}
}
//on keyboard press
on_keyboard_action(event) {
var k = event.keyCode;
//ctrl
if (k == 17 || event.metaKey || event.ctrlKey) {
if (this.ctrl_pressed == false)
this.ctrl_pressed = true;
}
//v
if (k == 86) {
if (this.Helper.is_input(document.activeElement)) {
return false;
}
if (this.ctrl_pressed == true && !window.Clipboard)
this.pasteCatcher.focus();
}
}
//on kaybord release
on_keyboardup_action(event) {
//ctrl
if (event.ctrlKey == false && this.ctrl_pressed == true) {
this.ctrl_pressed = false;
}
//command
else if (event.metaKey == false && this.command_pressed == true) {
this.command_pressed = false;
this.ctrl_pressed = false;
}
}
//draw image
paste_createImage(source) {
var pastedImage = new Image();
var _this = this;
pastedImage.onload = function () {
_this.on_paste(source, pastedImage.width, pastedImage.height);
};
pastedImage.src = source;
}
}
export default Clipboard_class;
@@ -0,0 +1,115 @@
/**
* Color Matrix
*
* A simplification of the color matrix class provided by
* EaselJS
*
* www.createjs.com/docs/easeljs/files/easeljs_filters_ColorFilter.js.html#l41
*/
class colorMatrix {
constructor(on_paste) {
this.DELTA_INDEX = [
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
10.0
];
}
multiply (a, b) {
var i, j, k, col = [];
for (i=0;i<5;i++) {
for (j=0;j<5;j++) {
col[j] = a[j+i*5];
}
for (j=0;j<5;j++) {
var val=0;
for (k=0;k<5;k++) {
val += b[j+k*5]*col[k];
}
a[j+i*5] = val;
}
}
}
colorMatrix (imageData, options) {
var brightness = options.brightness || 0;
var contrast = options.contrast || 0;
var matrix = [
1,0,0,0,0,
0,1,0,0,0,
0,0,1,0,0,
0,0,0,1,0,
0,0,0,0,1
];
// Contrast
var x;
if (contrast < 0) {
x = 127 + contrast / 100 * 127;
} else {
x = contrast % 1;
if (x == 0) {
x = this.DELTA_INDEX[contrast];
} else {
x = this.DELTA_INDEX[(contrast<<0)]*(1-x)+this.DELTA_INDEX[(contrast<<0)+1] * x;
}
x = x * 127 + 127;
}
this.multiply (matrix, [
x/127,0,0,0,0.5*(127-x),
0,x/127,0,0,0.5*(127-x),
0,0,x/127,0,0.5*(127-x),
0,0,0,1,0,
0,0,0,0,1
]);
// Brightness
this.multiply (matrix,[
1,0,0,0, brightness,
0,1,0,0, brightness,
0,0,1,0, brightness,
0,0,0,1,0,
0,0,0,0,1
])
// Apply Filter
var data = imageData.data;
var l = data.length;
var r,g,b,a;
var m0 = matrix[0], m1 = matrix[1], m2 = matrix[2], m3 = matrix[3], m4 = matrix[4];
var m5 = matrix[5], m6 = matrix[6], m7 = matrix[7], m8 = matrix[8], m9 = matrix[9];
var m10 = matrix[10], m11 = matrix[11], m12 = matrix[12], m13 = matrix[13], m14 = matrix[14];
var m15 = matrix[15], m16 = matrix[16], m17 = matrix[17], m18 = matrix[18], m19 = matrix[19];
for (var i=0; i<l; i+=4) {
r = data[i];
g = data[i+1];
b = data[i+2];
a = data[i+3];
data[i] = r*m0+g*m1+b*m2+a*m3+m4; // red
data[i+1] = r*m5+g*m6+b*m7+a*m8+m9; // green
data[i+2] = r*m10+g*m11+b*m12+a*m13+m14; // blue
data[i+3] = r*m15+g*m16+b*m17+a*m18+m19; // alpha
}
return imageData;
}
}
export default colorMatrix;
@@ -0,0 +1,653 @@
/*
* Color Thief v2.0
* by Lokesh Dhakar - http://www.lokeshdhakar.com
*
* Thanks
* ------
* Nick Rabinowitz - For creating quantize.js.
* John Schulz - For clean up and optimization. @JFSIII
* Nathan Spady - For adding drag and drop support to the demo page.
*
* License
* -------
* Copyright 2011, 2015 Lokesh Dhakar
* Released under the MIT license
* https://raw.githubusercontent.com/lokesh/color-thief/master/LICENSE
*
* @license
*/
/*
CanvasImage Class
Class that wraps the html image element and canvas.
It also simplifies some of the canvas context manipulation
with a set of helper functions.
*/
var CanvasImage = function (image) {
this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext('2d');
document.body.appendChild(this.canvas);
this.width = this.canvas.width = image.width;
this.height = this.canvas.height = image.height;
this.context.drawImage(image, 0, 0, this.width, this.height);
};
CanvasImage.prototype.clear = function () {
this.context.clearRect(0, 0, this.width, this.height);
};
CanvasImage.prototype.update = function (imageData) {
this.context.putImageData(imageData, 0, 0);
};
CanvasImage.prototype.getPixelCount = function () {
return this.width * this.height;
};
CanvasImage.prototype.getImageData = function () {
return this.context.getImageData(0, 0, this.width, this.height);
};
CanvasImage.prototype.removeCanvas = function () {
this.canvas.parentNode.removeChild(this.canvas);
};
var ColorThief = function () {};
/*
* getColor(sourceImage[, quality])
* returns {r: num, g: num, b: num}
*
* Use the median cut algorithm provided by quantize.js to cluster similar
* colors and return the base color from the largest cluster.
*
* Quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
* faster a color will be returned but the greater the likelihood that it will not be the visually
* most dominant color.
*
* */
ColorThief.prototype.getColor = function(sourceImage, quality) {
var palette = this.getPalette(sourceImage, 5, quality);
var dominantColor = palette[0];
return dominantColor;
};
/*
* getPalette(sourceImage[, colorCount, quality])
* returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
*
* Use the median cut algorithm provided by quantize.js to cluster similar colors.
*
* colorCount determines the size of the palette; the number of colors returned. If not set, it
* defaults to 10.
*
* BUGGY: Function does not always return the requested amount of colors. It can be +/- 2.
*
* quality is an optional argument. It needs to be an integer. 1 is the highest quality settings.
* 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
* faster the palette generation but the greater the likelihood that colors will be missed.
*
*
*/
ColorThief.prototype.getPalette = function(sourceImage, colorCount, quality) {
if (typeof colorCount === 'undefined' || colorCount < 2 || colorCount > 256) {
colorCount = 10;
}
if (typeof quality === 'undefined' || quality < 1) {
quality = 10;
}
// Create custom CanvasImage object
var image = new CanvasImage(sourceImage);
var imageData = image.getImageData();
var pixels = imageData.data;
var pixelCount = image.getPixelCount();
// Store the RGB values in an array format suitable for quantize function
var pixelArray = [];
for (var i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
offset = i * 4;
r = pixels[offset + 0];
g = pixels[offset + 1];
b = pixels[offset + 2];
a = pixels[offset + 3];
// If pixel is mostly opaque and not white
if (a >= 125) {
if (!(r > 250 && g > 250 && b > 250)) {
pixelArray.push([r, g, b]);
}
}
}
// Send array to quantize function which clusters values
// using median cut algorithm
var cmap = MMCQ.quantize(pixelArray, colorCount);
var palette = cmap? cmap.palette() : null;
// Clean up
image.removeCanvas();
return palette;
};
ColorThief.prototype.getColorFromUrl = function(imageUrl, callback, quality) {
sourceImage = document.createElement("img");
var thief = this;
sourceImage.addEventListener('load' , function(){
var palette = thief.getPalette(sourceImage, 5, quality);
var dominantColor = palette[0];
callback(dominantColor, imageUrl);
});
sourceImage.src = imageUrl
};
ColorThief.prototype.getImageData = function(imageUrl, callback) {
xhr = new XMLHttpRequest();
xhr.open('GET', imageUrl, true);
xhr.responseType = 'arraybuffer'
xhr.onload = function(e) {
if (this.status == 200) {
uInt8Array = new Uint8Array(this.response)
i = uInt8Array.length
binaryString = new Array(i);
for (var i = 0; i < uInt8Array.length; i++){
binaryString[i] = String.fromCharCode(uInt8Array[i])
}
data = binaryString.join('')
base64 = window.btoa(data)
callback ("data:image/png;base64,"+base64)
}
}
xhr.send();
};
ColorThief.prototype.getColorAsync = function(imageUrl, callback, quality) {
var thief = this;
this.getImageData(imageUrl, function(imageData){
sourceImage = document.createElement("img");
sourceImage.addEventListener('load' , function(){
var palette = thief.getPalette(sourceImage, 5, quality);
var dominantColor = palette[0];
callback(dominantColor, this);
});
sourceImage.src = imageData;
});
};
/*!
* quantize.js Copyright 2008 Nick Rabinowitz.
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
* @license
*/
// fill out a couple protovis dependencies
/*!
* Block below copied from Protovis: http://mbostock.github.com/protovis/
* Copyright 2010 Stanford Visualization Group
* Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php
* @license
*/
if (!pv) {
var pv = {
map: function(array, f) {
var o = {};
return f ? array.map(function(d, i) { o.index = i; return f.call(o, d); }) : array.slice();
},
naturalOrder: function(a, b) {
return (a < b) ? -1 : ((a > b) ? 1 : 0);
},
sum: function(array, f) {
var o = {};
return array.reduce(f ? function(p, d, i) { o.index = i; return p + f.call(o, d); } : function(p, d) { return p + d; }, 0);
},
max: function(array, f) {
return Math.max.apply(null, f ? pv.map(array, f) : array);
}
};
}
/**
* Basic Javascript port of the MMCQ (modified median cut quantization)
* algorithm from the Leptonica library (http://www.leptonica.com/).
* Returns a color map you can use to map original pixels to the reduced
* palette. Still a work in progress.
*
* @author Nick Rabinowitz
* @example
// array of pixels as [R,G,B] arrays
var myPixels = [[190,197,190], [202,204,200], [207,214,210], [211,214,211], [205,207,207]
// etc
];
var maxColors = 4;
var cmap = MMCQ.quantize(myPixels, maxColors);
var newPalette = cmap.palette();
var newPixels = myPixels.map(function(p) {
return cmap.map(p);
});
*/
var MMCQ = (function() {
// private constants
var sigbits = 5,
rshift = 8 - sigbits,
maxIterations = 1000,
fractByPopulations = 0.75;
// get reduced-space color index for a pixel
function getColorIndex(r, g, b) {
return (r << (2 * sigbits)) + (g << sigbits) + b;
}
// Simple priority queue
function PQueue(comparator) {
var contents = [],
sorted = false;
function sort() {
contents.sort(comparator);
sorted = true;
}
return {
push: function(o) {
contents.push(o);
sorted = false;
},
peek: function(index) {
if (!sorted) sort();
if (index===undefined) index = contents.length - 1;
return contents[index];
},
pop: function() {
if (!sorted) sort();
return contents.pop();
},
size: function() {
return contents.length;
},
map: function(f) {
return contents.map(f);
},
debug: function() {
if (!sorted) sort();
return contents;
}
};
}
// 3d color space box
function VBox(r1, r2, g1, g2, b1, b2, histo) {
var vbox = this;
vbox.r1 = r1;
vbox.r2 = r2;
vbox.g1 = g1;
vbox.g2 = g2;
vbox.b1 = b1;
vbox.b2 = b2;
vbox.histo = histo;
}
VBox.prototype = {
volume: function(force) {
var vbox = this;
if (!vbox._volume || force) {
vbox._volume = ((vbox.r2 - vbox.r1 + 1) * (vbox.g2 - vbox.g1 + 1) * (vbox.b2 - vbox.b1 + 1));
}
return vbox._volume;
},
count: function(force) {
var vbox = this,
histo = vbox.histo;
if (!vbox._count_set || force) {
var npix = 0,
index, i, j, k;
for (i = vbox.r1; i <= vbox.r2; i++) {
for (j = vbox.g1; j <= vbox.g2; j++) {
for (k = vbox.b1; k <= vbox.b2; k++) {
index = getColorIndex(i,j,k);
npix += (histo[index] || 0);
}
}
}
vbox._count = npix;
vbox._count_set = true;
}
return vbox._count;
},
copy: function() {
var vbox = this;
return new VBox(vbox.r1, vbox.r2, vbox.g1, vbox.g2, vbox.b1, vbox.b2, vbox.histo);
},
avg: function(force) {
var vbox = this,
histo = vbox.histo;
if (!vbox._avg || force) {
var ntot = 0,
mult = 1 << (8 - sigbits),
rsum = 0,
gsum = 0,
bsum = 0,
hval,
i, j, k, histoindex;
for (i = vbox.r1; i <= vbox.r2; i++) {
for (j = vbox.g1; j <= vbox.g2; j++) {
for (k = vbox.b1; k <= vbox.b2; k++) {
histoindex = getColorIndex(i,j,k);
hval = histo[histoindex] || 0;
ntot += hval;
rsum += (hval * (i + 0.5) * mult);
gsum += (hval * (j + 0.5) * mult);
bsum += (hval * (k + 0.5) * mult);
}
}
}
if (ntot) {
vbox._avg = [~~(rsum/ntot), ~~(gsum/ntot), ~~(bsum/ntot)];
} else {
vbox._avg = [
~~(mult * (vbox.r1 + vbox.r2 + 1) / 2),
~~(mult * (vbox.g1 + vbox.g2 + 1) / 2),
~~(mult * (vbox.b1 + vbox.b2 + 1) / 2)
];
}
}
return vbox._avg;
},
contains: function(pixel) {
var vbox = this,
rval = pixel[0] >> rshift;
gval = pixel[1] >> rshift;
bval = pixel[2] >> rshift;
return (rval >= vbox.r1 && rval <= vbox.r2 &&
gval >= vbox.g1 && gval <= vbox.g2 &&
bval >= vbox.b1 && bval <= vbox.b2);
}
};
// Color map
function CMap() {
this.vboxes = new PQueue(function(a,b) {
return pv.naturalOrder(
a.vbox.count()*a.vbox.volume(),
b.vbox.count()*b.vbox.volume()
);
});
}
CMap.prototype = {
push: function(vbox) {
this.vboxes.push({
vbox: vbox,
color: vbox.avg()
});
},
palette: function() {
return this.vboxes.map(function(vb) { return vb.color; });
},
size: function() {
return this.vboxes.size();
},
map: function(color) {
var vboxes = this.vboxes;
for (var i=0; i<vboxes.size(); i++) {
if (vboxes.peek(i).vbox.contains(color)) {
return vboxes.peek(i).color;
}
}
return this.nearest(color);
},
nearest: function(color) {
var vboxes = this.vboxes,
d1, d2, pColor;
for (var i=0; i<vboxes.size(); i++) {
d2 = Math.sqrt(
Math.pow(color[0] - vboxes.peek(i).color[0], 2) +
Math.pow(color[1] - vboxes.peek(i).color[1], 2) +
Math.pow(color[2] - vboxes.peek(i).color[2], 2)
);
if (d2 < d1 || d1 === undefined) {
d1 = d2;
pColor = vboxes.peek(i).color;
}
}
return pColor;
},
forcebw: function() {
// XXX: won't work yet
var vboxes = this.vboxes;
vboxes.sort(function(a,b) { return pv.naturalOrder(pv.sum(a.color), pv.sum(b.color));});
// force darkest color to black if everything < 5
var lowest = vboxes[0].color;
if (lowest[0] < 5 && lowest[1] < 5 && lowest[2] < 5)
vboxes[0].color = [0,0,0];
// force lightest color to white if everything > 251
var idx = vboxes.length-1,
highest = vboxes[idx].color;
if (highest[0] > 251 && highest[1] > 251 && highest[2] > 251)
vboxes[idx].color = [255,255,255];
}
};
// histo (1-d array, giving the number of pixels in
// each quantized region of color space), or null on error
function getHisto(pixels) {
var histosize = 1 << (3 * sigbits),
histo = new Array(histosize),
index, rval, gval, bval;
pixels.forEach(function(pixel) {
rval = pixel[0] >> rshift;
gval = pixel[1] >> rshift;
bval = pixel[2] >> rshift;
index = getColorIndex(rval, gval, bval);
histo[index] = (histo[index] || 0) + 1;
});
return histo;
}
function vboxFromPixels(pixels, histo) {
var rmin=1000000, rmax=0,
gmin=1000000, gmax=0,
bmin=1000000, bmax=0,
rval, gval, bval;
// find min/max
pixels.forEach(function(pixel) {
rval = pixel[0] >> rshift;
gval = pixel[1] >> rshift;
bval = pixel[2] >> rshift;
if (rval < rmin) rmin = rval;
else if (rval > rmax) rmax = rval;
if (gval < gmin) gmin = gval;
else if (gval > gmax) gmax = gval;
if (bval < bmin) bmin = bval;
else if (bval > bmax) bmax = bval;
});
return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo);
}
function medianCutApply(histo, vbox) {
if (!vbox.count()) return;
var rw = vbox.r2 - vbox.r1 + 1,
gw = vbox.g2 - vbox.g1 + 1,
bw = vbox.b2 - vbox.b1 + 1,
maxw = pv.max([rw, gw, bw]);
// only one pixel, no split
if (vbox.count() == 1) {
return [vbox.copy()];
}
/* Find the partial sum arrays along the selected axis. */
var total = 0,
partialsum = [],
lookaheadsum = [],
i, j, k, sum, index;
if (maxw == rw) {
for (i = vbox.r1; i <= vbox.r2; i++) {
sum = 0;
for (j = vbox.g1; j <= vbox.g2; j++) {
for (k = vbox.b1; k <= vbox.b2; k++) {
index = getColorIndex(i,j,k);
sum += (histo[index] || 0);
}
}
total += sum;
partialsum[i] = total;
}
}
else if (maxw == gw) {
for (i = vbox.g1; i <= vbox.g2; i++) {
sum = 0;
for (j = vbox.r1; j <= vbox.r2; j++) {
for (k = vbox.b1; k <= vbox.b2; k++) {
index = getColorIndex(j,i,k);
sum += (histo[index] || 0);
}
}
total += sum;
partialsum[i] = total;
}
}
else { /* maxw == bw */
for (i = vbox.b1; i <= vbox.b2; i++) {
sum = 0;
for (j = vbox.r1; j <= vbox.r2; j++) {
for (k = vbox.g1; k <= vbox.g2; k++) {
index = getColorIndex(j,k,i);
sum += (histo[index] || 0);
}
}
total += sum;
partialsum[i] = total;
}
}
partialsum.forEach(function(d,i) {
lookaheadsum[i] = total-d;
});
function doCut(color) {
var dim1 = color + '1',
dim2 = color + '2',
left, right, vbox1, vbox2, d2, count2=0;
for (i = vbox[dim1]; i <= vbox[dim2]; i++) {
if (partialsum[i] > total / 2) {
vbox1 = vbox.copy();
vbox2 = vbox.copy();
left = i - vbox[dim1];
right = vbox[dim2] - i;
if (left <= right)
d2 = Math.min(vbox[dim2] - 1, ~~(i + right / 2));
else d2 = Math.max(vbox[dim1], ~~(i - 1 - left / 2));
// avoid 0-count boxes
while (!partialsum[d2]) d2++;
count2 = lookaheadsum[d2];
while (!count2 && partialsum[d2-1]) count2 = lookaheadsum[--d2];
// set dimensions
vbox1[dim2] = d2;
vbox2[dim1] = vbox1[dim2] + 1;
return [vbox1, vbox2];
}
}
}
// determine the cut planes
return maxw == rw ? doCut('r') :
maxw == gw ? doCut('g') :
doCut('b');
}
function quantize(pixels, maxcolors) {
// short-circuit
if (!pixels.length || maxcolors < 2 || maxcolors > 256) {
return false;
}
// XXX: check color content and convert to grayscale if insufficient
var histo = getHisto(pixels),
histosize = 1 << (3 * sigbits);
// check that we aren't below maxcolors already
var nColors = 0;
histo.forEach(function() { nColors++; });
if (nColors <= maxcolors) {
// XXX: generate the new colors from the histo and return
}
// get the beginning vbox from the colors
var vbox = vboxFromPixels(pixels, histo),
pq = new PQueue(function(a,b) { return pv.naturalOrder(a.count(), b.count()); });
pq.push(vbox);
// inner function to do the iteration
function iter(lh, target) {
var ncolors = 1,
niters = 0,
vbox;
while (niters < maxIterations) {
vbox = lh.pop();
if (!vbox.count()) { /* just put it back */
lh.push(vbox);
niters++;
continue;
}
// do the cut
var vboxes = medianCutApply(histo, vbox),
vbox1 = vboxes[0],
vbox2 = vboxes[1];
if (!vbox1) {
return;
}
lh.push(vbox1);
if (vbox2) { /* vbox2 can be null */
lh.push(vbox2);
ncolors++;
}
if (ncolors >= target) return;
if (niters++ > maxIterations) {
return;
}
}
}
// first set of colors, sorted by population
iter(pq, fractByPopulations * maxcolors);
// Re-sort by the product of pixel occupancy times the size in color space.
var pq2 = new PQueue(function(a,b) {
return pv.naturalOrder(a.count()*a.volume(), b.count()*b.volume());
});
while (pq.size()) {
pq2.push(pq.pop());
}
// next set - generate the median cuts using the (npix * vol) sorting.
iter(pq2, maxcolors - pq2.size());
// calculate the actual colors
var cmap = new CMap();
while (pq2.size()) {
cmap.push(pq2.pop());
}
return cmap;
}
return {
quantize: quantize
};
})();
export default ColorThief;
@@ -0,0 +1,106 @@
/**
* Color conversion and Pantone matching utilities.
*
* hexToRgb(hex) → { r, g, b }
* rgbToHsl(r,g,b) → { h, s, l } (h=0-360, s/l=0-100)
* rgbToLab(r,g,b) → { L, a, b } (CIE LAB D65)
* deltaE(lab1, lab2) → number (CIE76, lower = more similar)
* nearestPantone(hex) → { name, hex, deltaE }
*/
import PANTONE_COLORS from './../data/pantone_colors.js';
// Pre-convert Pantone database to LAB once at module load
const _pantonelab = PANTONE_COLORS.map(([name, hex]) => {
const { r, g, b } = hexToRgb(hex);
return { name, hex, lab: rgbToLab(r, g, b) };
});
export function hexToRgb(hex) {
const h = hex.replace('#', '');
const n = parseInt(h.length === 3
? h.split('').map(c => c + c).join('')
: h, 16);
return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
}
export function rgbToHex(r, g, b) {
return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('');
}
export function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
}
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
}
export function rgbToLab(r, g, b) {
// sRGB → linear
let R = r / 255, G = g / 255, B = b / 255;
R = R > 0.04045 ? Math.pow((R + 0.055) / 1.055, 2.4) : R / 12.92;
G = G > 0.04045 ? Math.pow((G + 0.055) / 1.055, 2.4) : G / 12.92;
B = B > 0.04045 ? Math.pow((B + 0.055) / 1.055, 2.4) : B / 12.92;
// linear RGB → XYZ (D65)
let X = R * 0.4124564 + G * 0.3575761 + B * 0.1804375;
let Y = R * 0.2126729 + G * 0.7151522 + B * 0.0721750;
let Z = R * 0.0193339 + G * 0.1191920 + B * 0.9503041;
// XYZ → LAB (D65 white = 0.95047, 1.0, 1.08883)
const f = v => v > 0.008856 ? Math.cbrt(v) : 7.787 * v + 16 / 116;
X = f(X / 0.95047); Y = f(Y / 1.0); Z = f(Z / 1.08883);
return { L: 116 * Y - 16, a: 500 * (X - Y), b: 200 * (Y - Z) };
}
export function deltaE(lab1, lab2) {
const dL = lab1.L - lab2.L;
const da = lab1.a - lab2.a;
const db = lab1.b - lab2.b;
return Math.sqrt(dL * dL + da * da + db * db);
}
/**
* Find the closest Pantone color to a hex value.
* Returns { name, hex, deltaE, quality }
* quality: 'excellent' (<2), 'good' (2-5), 'fair' (5-10), 'poor' (>10)
*/
export function nearestPantone(hex) {
const { r, g, b } = hexToRgb(hex);
const lab = rgbToLab(r, g, b);
let best = null, bestDE = Infinity;
for (const entry of _pantonelab) {
const de = deltaE(lab, entry.lab);
if (de < bestDE) { bestDE = de; best = entry; }
}
const de = Math.round(bestDE * 10) / 10;
const quality = de < 2 ? 'excellent' : de < 5 ? 'good' : de < 10 ? 'fair' : 'poor';
return { name: best.name, hex: best.hex, deltaE: de, quality };
}
/**
* Quality label + color for ΔE badge.
*/
export function deltaEBadge(quality) {
const map = {
excellent: { label: 'Excellent match', color: '#4ade80' },
good: { label: 'Good match', color: '#86efac' },
fair: { label: 'Fair match', color: '#fbbf24' },
poor: { label: 'Poor match — color may shift in print', color: '#f87171' },
};
return map[quality] || map.poor;
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+61
View File
@@ -0,0 +1,61 @@
/*
* glfx.js
* http://evanw.github.com/glfx.js/
*
* Copyright 2011 Evan Wallace
* Released under the MIT license
*/
var fx=function(){function q(a,d,c){return Math.max(a,Math.min(d,c))}function w(b){return{_:b,loadContentsOf:function(b){a=this._.gl;this._.loadContentsOf(b)},destroy:function(){a=this._.gl;this._.destroy()}}}function A(a){return w(r.fromElement(a))}function B(b,d){var c=a.UNSIGNED_BYTE;if(a.getExtension("OES_texture_float")&&a.getExtension("OES_texture_float_linear")){var e=new r(100,100,a.RGBA,a.FLOAT);try{e.drawTo(function(){c=a.FLOAT})}catch(g){}e.destroy()}this._.texture&&this._.texture.destroy();
this._.spareTexture&&this._.spareTexture.destroy();this.width=b;this.height=d;this._.texture=new r(b,d,a.RGBA,c);this._.spareTexture=new r(b,d,a.RGBA,c);this._.extraTexture=this._.extraTexture||new r(0,0,a.RGBA,c);this._.flippedShader=this._.flippedShader||new h(null,"uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,vec2(texCoord.x,1.0-texCoord.y));}");this._.isInitialized=!0}function C(a,d,c){this._.isInitialized&&
a._.width==this.width&&a._.height==this.height||B.call(this,d?d:a._.width,c?c:a._.height);a._.use();this._.texture.drawTo(function(){h.getDefaultShader().drawRect()});return this}function D(){this._.texture.use();this._.flippedShader.drawRect();return this}function f(a,d,c,e){(c||this._.texture).use();this._.spareTexture.drawTo(function(){a.uniforms(d).drawRect()});this._.spareTexture.swapWith(e||this._.texture)}function E(a){a.parentNode.insertBefore(this,a);a.parentNode.removeChild(a);return this}
function F(){var b=new r(this._.texture.width,this._.texture.height,a.RGBA,a.UNSIGNED_BYTE);this._.texture.use();b.drawTo(function(){h.getDefaultShader().drawRect()});return w(b)}function G(){var b=this._.texture.width,d=this._.texture.height,c=new Uint8Array(4*b*d);this._.texture.drawTo(function(){a.readPixels(0,0,b,d,a.RGBA,a.UNSIGNED_BYTE,c)});return c}function k(b){return function(){a=this._.gl;return b.apply(this,arguments)}}function x(a,d,c,e,g,l,n,p){var m=c-g,h=e-l,f=n-g,k=p-l;g=a-c+g-n;l=
d-e+l-p;var q=m*k-f*h,f=(g*k-f*l)/q,m=(m*l-g*h)/q;return[c-a+f*c,e-d+f*e,f,n-a+m*n,p-d+m*p,m,a,d,1]}function y(a){var d=a[0],c=a[1],e=a[2],g=a[3],l=a[4],n=a[5],p=a[6],m=a[7];a=a[8];var f=d*l*a-d*n*m-c*g*a+c*n*p+e*g*m-e*l*p;return[(l*a-n*m)/f,(e*m-c*a)/f,(c*n-e*l)/f,(n*p-g*a)/f,(d*a-e*p)/f,(e*g-d*n)/f,(g*m-l*p)/f,(c*p-d*m)/f,(d*l-c*g)/f]}function z(a){var d=a.length;this.xa=[];this.ya=[];this.u=[];this.y2=[];a.sort(function(a,b){return a[0]-b[0]});for(var c=0;c<d;c++)this.xa.push(a[c][0]),this.ya.push(a[c][1]);
this.u[0]=0;this.y2[0]=0;for(c=1;c<d-1;++c){a=this.xa[c+1]-this.xa[c-1];var e=(this.xa[c]-this.xa[c-1])/a,g=e*this.y2[c-1]+2;this.y2[c]=(e-1)/g;this.u[c]=(6*((this.ya[c+1]-this.ya[c])/(this.xa[c+1]-this.xa[c])-(this.ya[c]-this.ya[c-1])/(this.xa[c]-this.xa[c-1]))/a-e*this.u[c-1])/g}this.y2[d-1]=0;for(c=d-2;0<=c;--c)this.y2[c]=this.y2[c]*this.y2[c+1]+this.u[c]}function u(a,d){return new h(null,a+"uniform sampler2D texture;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 coord=texCoord*texSize;"+
d+"gl_FragColor=texture2D(texture,coord/texSize);vec2 clampedCoord=clamp(coord,vec2(0.0),texSize);if(coord!=clampedCoord){gl_FragColor.a*=max(0.0,1.0-length(coord-clampedCoord));}}")}function H(b,d){a.brightnessContrast=a.brightnessContrast||new h(null,"uniform sampler2D texture;uniform float brightness;uniform float contrast;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.rgb+=brightness;if(contrast>0.0){color.rgb=(color.rgb-0.5)/(1.0-contrast)+0.5;}else{color.rgb=(color.rgb-0.5)*(1.0+contrast)+0.5;}gl_FragColor=color;}");
f.call(this,a.brightnessContrast,{brightness:q(-1,b,1),contrast:q(-1,d,1)});return this}function t(a){a=new z(a);for(var d=[],c=0;256>c;c++)d.push(q(0,Math.floor(256*a.interpolate(c/255)),255));return d}function I(b,d,c){b=t(b);1==arguments.length?d=c=b:(d=t(d),c=t(c));for(var e=[],g=0;256>g;g++)e.splice(e.length,0,b[g],d[g],c[g],255);this._.extraTexture.initFromBytes(256,1,e);this._.extraTexture.use(1);a.curves=a.curves||new h(null,"uniform sampler2D texture;uniform sampler2D map;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color.r=texture2D(map,vec2(color.r)).r;color.g=texture2D(map,vec2(color.g)).g;color.b=texture2D(map,vec2(color.b)).b;gl_FragColor=color;}");
a.curves.textures({map:1});f.call(this,a.curves,{});return this}function J(b){a.denoise=a.denoise||new h(null,"uniform sampler2D texture;uniform float exponent;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec4 center=texture2D(texture,texCoord);vec4 color=vec4(0.0);float total=0.0;for(float x=-4.0;x<=4.0;x+=1.0){for(float y=-4.0;y<=4.0;y+=1.0){vec4 sample=texture2D(texture,texCoord+vec2(x,y)/texSize);float weight=1.0-abs(dot(sample.rgb-center.rgb,vec3(0.25)));weight=pow(weight,exponent);color+=sample*weight;total+=weight;}}gl_FragColor=color/total;}");
for(var d=0;2>d;d++)f.call(this,a.denoise,{exponent:Math.max(0,b),texSize:[this.width,this.height]});return this}function K(b,d){a.hueSaturation=a.hueSaturation||new h(null,"uniform sampler2D texture;uniform float hue;uniform float saturation;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float angle=hue*3.14159265;float s=sin(angle),c=cos(angle);vec3 weights=(vec3(2.0*c,-sqrt(3.0)*s-c,sqrt(3.0)*s-c)+1.0)/3.0;float len=length(color.rgb);color.rgb=vec3(dot(color.rgb,weights.xyz),dot(color.rgb,weights.zxy),dot(color.rgb,weights.yzx));float average=(color.r+color.g+color.b)/3.0;if(saturation>0.0){color.rgb+=(average-color.rgb)*(1.0-1.0/(1.001-saturation));}else{color.rgb+=(average-color.rgb)*(-saturation);}gl_FragColor=color;}");
f.call(this,a.hueSaturation,{hue:q(-1,b,1),saturation:q(-1,d,1)});return this}function L(b){a.noise=a.noise||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;float rand(vec2 co){return fract(sin(dot(co.xy,vec2(12.9898,78.233)))*43758.5453);}void main(){vec4 color=texture2D(texture,texCoord);float diff=(rand(texCoord)-0.5)*amount;color.r+=diff;color.g+=diff;color.b+=diff;gl_FragColor=color;}");
f.call(this,a.noise,{amount:q(0,b,1)});return this}function M(b){a.sepia=a.sepia||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float r=color.r;float g=color.g;float b=color.b;color.r=min(1.0,(r*(1.0-(0.607*amount)))+(g*(0.769*amount))+(b*(0.189*amount)));color.g=min(1.0,(r*0.349*amount)+(g*(1.0-(0.314*amount)))+(b*0.168*amount));color.b=min(1.0,(r*0.272*amount)+(g*0.534*amount)+(b*(1.0-(0.869*amount))));gl_FragColor=color;}");
f.call(this,a.sepia,{amount:q(0,b,1)});return this}function N(b,d){a.unsharpMask=a.unsharpMask||new h(null,"uniform sampler2D blurredTexture;uniform sampler2D originalTexture;uniform float strength;uniform float threshold;varying vec2 texCoord;void main(){vec4 blurred=texture2D(blurredTexture,texCoord);vec4 original=texture2D(originalTexture,texCoord);gl_FragColor=mix(blurred,original,1.0+strength);}");
this._.extraTexture.ensureFormat(this._.texture);this._.texture.use();this._.extraTexture.drawTo(function(){h.getDefaultShader().drawRect()});this._.extraTexture.use(1);this.triangleBlur(b);a.unsharpMask.textures({originalTexture:1});f.call(this,a.unsharpMask,{strength:d});this._.extraTexture.unuse(1);return this}function O(b){a.vibrance=a.vibrance||new h(null,"uniform sampler2D texture;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;float mx=max(color.r,max(color.g,color.b));float amt=(mx-average)*(-amount*3.0);color.rgb=mix(color.rgb,vec3(mx),amt);gl_FragColor=color;}");
f.call(this,a.vibrance,{amount:q(-1,b,1)});return this}function P(b,d){a.vignette=a.vignette||new h(null,"uniform sampler2D texture;uniform float size;uniform float amount;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);float dist=distance(texCoord,vec2(0.5,0.5));color.rgb*=smoothstep(0.8,size*0.799,dist*(amount+size));gl_FragColor=color;}");
f.call(this,a.vignette,{size:q(0,b,1),amount:q(0,d,1)});return this}function Q(b,d,c){a.lensBlurPrePass=a.lensBlurPrePass||new h(null,"uniform sampler2D texture;uniform float power;varying vec2 texCoord;void main(){vec4 color=texture2D(texture,texCoord);color=pow(color,vec4(power));gl_FragColor=vec4(color);}");var e="uniform sampler2D texture0;uniform sampler2D texture1;uniform vec2 delta0;uniform vec2 delta1;uniform float power;varying vec2 texCoord;"+
s+"vec4 sample(vec2 delta){float offset=random(vec3(delta,151.7182),0.0);vec4 color=vec4(0.0);float total=0.0;for(float t=0.0;t<=30.0;t++){float percent=(t+offset)/30.0;color+=texture2D(texture0,texCoord+delta*percent);total+=1.0;}return color/total;}";
a.lensBlur0=a.lensBlur0||new h(null,e+"void main(){gl_FragColor=sample(delta0);}");a.lensBlur1=a.lensBlur1||new h(null,e+"void main(){gl_FragColor=(sample(delta0)+sample(delta1))*0.5;}");a.lensBlur2=a.lensBlur2||(new h(null,e+"void main(){vec4 color=(sample(delta0)+2.0*texture2D(texture1,texCoord))/3.0;gl_FragColor=pow(color,vec4(power));}")).textures({texture1:1});for(var e=
[],g=0;3>g;g++){var l=c+2*g*Math.PI/3;e.push([b*Math.sin(l)/this.width,b*Math.cos(l)/this.height])}b=Math.pow(10,q(-1,d,1));f.call(this,a.lensBlurPrePass,{power:b});this._.extraTexture.ensureFormat(this._.texture);f.call(this,a.lensBlur0,{delta0:e[0]},this._.texture,this._.extraTexture);f.call(this,a.lensBlur1,{delta0:e[1],delta1:e[2]},this._.extraTexture,this._.extraTexture);f.call(this,a.lensBlur0,{delta0:e[1]});this._.extraTexture.use(1);f.call(this,a.lensBlur2,{power:1/b,delta0:e[2]});return this}
function R(b,d,c,e,g,l){a.tiltShift=a.tiltShift||new h(null,"uniform sampler2D texture;uniform float blurRadius;uniform float gradientRadius;uniform vec2 start;uniform vec2 end;uniform vec2 delta;uniform vec2 texSize;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);vec2 normal=normalize(vec2(start.y-end.y,end.x-start.x));float radius=smoothstep(0.0,1.0,abs(dot(texCoord*texSize-start,normal))/gradientRadius)*blurRadius;for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta/texSize*percent*radius);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
var n=c-b,p=e-d,m=Math.sqrt(n*n+p*p);f.call(this,a.tiltShift,{blurRadius:g,gradientRadius:l,start:[b,d],end:[c,e],delta:[n/m,p/m],texSize:[this.width,this.height]});f.call(this,a.tiltShift,{blurRadius:g,gradientRadius:l,start:[b,d],end:[c,e],delta:[-p/m,n/m],texSize:[this.width,this.height]});return this}function S(b){a.triangleBlur=a.triangleBlur||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec4 sample=texture2D(texture,texCoord+delta*percent);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
f.call(this,a.triangleBlur,{delta:[b/this.width,0]});f.call(this,a.triangleBlur,{delta:[0,b/this.height]});return this}function T(b,d,c){a.zoomBlur=a.zoomBlur||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;"+s+"void main(){vec4 color=vec4(0.0);float total=0.0;vec2 toCenter=center-texCoord*texSize;float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=0.0;t<=40.0;t++){float percent=(t+offset)/40.0;float weight=4.0*(percent-percent*percent);vec4 sample=texture2D(texture,texCoord+toCenter*percent*strength/texSize);sample.rgb*=sample.a;color+=sample*weight;total+=weight;}gl_FragColor=color/total;gl_FragColor.rgb/=gl_FragColor.a+0.00001;}");
f.call(this,a.zoomBlur,{center:[b,d],strength:c,texSize:[this.width,this.height]});return this}function U(b,d,c,e){a.colorHalftone=a.colorHalftone||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(float angle){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);vec3 cmy=1.0-color.rgb;float k=min(cmy.x,min(cmy.y,cmy.z));cmy=(cmy-k)/(1.0-k);cmy=clamp(cmy*10.0-3.0+vec3(pattern(angle+0.26179),pattern(angle+1.30899),pattern(angle)),0.0,1.0);k=clamp(k*10.0-5.0+pattern(angle+0.78539),0.0,1.0);gl_FragColor=vec4(1.0-cmy-k,color.a);}");
f.call(this,a.colorHalftone,{center:[b,d],angle:c,scale:Math.PI/e,texSize:[this.width,this.height]});return this}function V(b,d,c,e){a.dotScreen=a.dotScreen||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float angle;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;float pattern(){float s=sin(angle),c=cos(angle);vec2 tex=texCoord*texSize-center;vec2 point=vec2(c*tex.x-s*tex.y,s*tex.x+c*tex.y)*scale;return(sin(point.x)*sin(point.y))*4.0;}void main(){vec4 color=texture2D(texture,texCoord);float average=(color.r+color.g+color.b)/3.0;gl_FragColor=vec4(vec3(average*10.0-5.0+pattern()),color.a);}");
f.call(this,a.dotScreen,{center:[b,d],angle:c,scale:Math.PI/e,texSize:[this.width,this.height]});return this}function W(b){a.edgeWork1=a.edgeWork1||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec3 sample=texture2D(texture,texCoord+delta*percent).rgb;float average=(sample.r+sample.g+sample.b)/3.0;color.x+=average*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=average*weight;total.y+=weight;}}gl_FragColor=vec4(color/total,0.0,1.0);}");
a.edgeWork2=a.edgeWork2||new h(null,"uniform sampler2D texture;uniform vec2 delta;varying vec2 texCoord;"+s+"void main(){vec2 color=vec2(0.0);vec2 total=vec2(0.0);float offset=random(vec3(12.9898,78.233,151.7182),0.0);for(float t=-30.0;t<=30.0;t++){float percent=(t+offset-0.5)/30.0;float weight=1.0-abs(percent);vec2 sample=texture2D(texture,texCoord+delta*percent).xy;color.x+=sample.x*weight;total.x+=weight;if(abs(t)<15.0){weight=weight*2.0-1.0;color.y+=sample.y*weight;total.y+=weight;}}float c=clamp(10000.0*(color.y/total.y-color.x/total.x)+0.5,0.0,1.0);gl_FragColor=vec4(c,c,c,1.0);}");
f.call(this,a.edgeWork1,{delta:[b/this.width,0]});f.call(this,a.edgeWork2,{delta:[0,b/this.height]});return this}function X(b,d,c){a.hexagonalPixelate=a.hexagonalPixelate||new h(null,"uniform sampler2D texture;uniform vec2 center;uniform float scale;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 tex=(texCoord*texSize-center)/scale;tex.y/=0.866025404;tex.x-=tex.y*0.5;vec2 a;if(tex.x+tex.y-floor(tex.x)-floor(tex.y)<1.0)a=vec2(floor(tex.x),floor(tex.y));else a=vec2(ceil(tex.x),ceil(tex.y));vec2 b=vec2(ceil(tex.x),floor(tex.y));vec2 c=vec2(floor(tex.x),ceil(tex.y));vec3 TEX=vec3(tex.x,tex.y,1.0-tex.x-tex.y);vec3 A=vec3(a.x,a.y,1.0-a.x-a.y);vec3 B=vec3(b.x,b.y,1.0-b.x-b.y);vec3 C=vec3(c.x,c.y,1.0-c.x-c.y);float alen=length(TEX-A);float blen=length(TEX-B);float clen=length(TEX-C);vec2 choice;if(alen<blen){if(alen<clen)choice=a;else choice=c;}else{if(blen<clen)choice=b;else choice=c;}choice.x+=choice.y*0.5;choice.y*=0.866025404;choice*=scale/texSize;gl_FragColor=texture2D(texture,choice+center/texSize);}");
f.call(this,a.hexagonalPixelate,{center:[b,d],scale:c,texSize:[this.width,this.height]});return this}function Y(b){a.ink=a.ink||new h(null,"uniform sampler2D texture;uniform float strength;uniform vec2 texSize;varying vec2 texCoord;void main(){vec2 dx=vec2(1.0/texSize.x,0.0);vec2 dy=vec2(0.0,1.0/texSize.y);vec4 color=texture2D(texture,texCoord);float bigTotal=0.0;float smallTotal=0.0;vec3 bigAverage=vec3(0.0);vec3 smallAverage=vec3(0.0);for(float x=-2.0;x<=2.0;x+=1.0){for(float y=-2.0;y<=2.0;y+=1.0){vec3 sample=texture2D(texture,texCoord+dx*x+dy*y).rgb;bigAverage+=sample;bigTotal+=1.0;if(abs(x)+abs(y)<2.0){smallAverage+=sample;smallTotal+=1.0;}}}vec3 edge=max(vec3(0.0),bigAverage/bigTotal-smallAverage/smallTotal);gl_FragColor=vec4(color.rgb-dot(edge,edge)*strength*100000.0,color.a);}");
f.call(this,a.ink,{strength:b*b*b*b*b,texSize:[this.width,this.height]});return this}function Z(b,d,c,e){a.bulgePinch=a.bulgePinch||u("uniform float radius;uniform float strength;uniform vec2 center;","coord-=center;float distance=length(coord);if(distance<radius){float percent=distance/radius;if(strength>0.0){coord*=mix(1.0,smoothstep(0.0,radius/distance,percent),strength*0.75);}else{coord*=mix(1.0,pow(percent,1.0+strength*0.75)*radius/distance,1.0-percent);}}coord+=center;");
f.call(this,a.bulgePinch,{radius:c,strength:q(-1,e,1),center:[b,d],texSize:[this.width,this.height]});return this}function $(b,d,c){a.matrixWarp=a.matrixWarp||u("uniform mat3 matrix;uniform bool useTextureSpace;","if(useTextureSpace)coord=coord/texSize*2.0-1.0;vec3 warp=matrix*vec3(coord,1.0);coord=warp.xy/warp.z;if(useTextureSpace)coord=(coord*0.5+0.5)*texSize;");b=Array.prototype.concat.apply([],b);if(4==b.length)b=
[b[0],b[1],0,b[2],b[3],0,0,0,1];else if(9!=b.length)throw"can only warp with 2x2 or 3x3 matrix";f.call(this,a.matrixWarp,{matrix:d?y(b):b,texSize:[this.width,this.height],useTextureSpace:c|0});return this}function aa(a,d){var c=x.apply(null,d),e=x.apply(null,a),c=y(c);return this.matrixWarp([c[0]*e[0]+c[1]*e[3]+c[2]*e[6],c[0]*e[1]+c[1]*e[4]+c[2]*e[7],c[0]*e[2]+c[1]*e[5]+c[2]*e[8],c[3]*e[0]+c[4]*e[3]+c[5]*e[6],c[3]*e[1]+c[4]*e[4]+c[5]*e[7],c[3]*e[2]+c[4]*e[5]+c[5]*e[8],c[6]*e[0]+c[7]*e[3]+c[8]*e[6],
c[6]*e[1]+c[7]*e[4]+c[8]*e[7],c[6]*e[2]+c[7]*e[5]+c[8]*e[8]])}function ba(b,d,c,e){a.swirl=a.swirl||u("uniform float radius;uniform float angle;uniform vec2 center;","coord-=center;float distance=length(coord);if(distance<radius){float percent=(radius-distance)/radius;float theta=percent*percent*angle;float s=sin(theta);float c=cos(theta);coord=vec2(coord.x*c-coord.y*s,coord.x*s+coord.y*c);}coord+=center;");
f.call(this,a.swirl,{radius:c,center:[b,d],angle:e,texSize:[this.width,this.height]});return this}var v={};(function(){function a(b){if(!b.getExtension("OES_texture_float"))return!1;var c=b.createFramebuffer(),e=b.createTexture();b.bindTexture(b.TEXTURE_2D,e);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.NEAREST);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);
b.texImage2D(b.TEXTURE_2D,0,b.RGBA,1,1,0,b.RGBA,b.UNSIGNED_BYTE,null);b.bindFramebuffer(b.FRAMEBUFFER,c);b.framebufferTexture2D(b.FRAMEBUFFER,b.COLOR_ATTACHMENT0,b.TEXTURE_2D,e,0);c=b.createTexture();b.bindTexture(b.TEXTURE_2D,c);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MAG_FILTER,b.LINEAR);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_MIN_FILTER,b.LINEAR);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_S,b.CLAMP_TO_EDGE);b.texParameteri(b.TEXTURE_2D,b.TEXTURE_WRAP_T,b.CLAMP_TO_EDGE);b.texImage2D(b.TEXTURE_2D,
0,b.RGBA,2,2,0,b.RGBA,b.FLOAT,new Float32Array([2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]));var e=b.createProgram(),d=b.createShader(b.VERTEX_SHADER),g=b.createShader(b.FRAGMENT_SHADER);b.shaderSource(d,"attribute vec2 vertex;void main(){gl_Position=vec4(vertex,0.0,1.0);}");b.shaderSource(g,"uniform sampler2D texture;void main(){gl_FragColor=texture2D(texture,vec2(0.5));}");b.compileShader(d);b.compileShader(g);b.attachShader(e,d);b.attachShader(e,
g);b.linkProgram(e);d=b.createBuffer();b.bindBuffer(b.ARRAY_BUFFER,d);b.bufferData(b.ARRAY_BUFFER,new Float32Array([0,0]),b.STREAM_DRAW);b.enableVertexAttribArray(0);b.vertexAttribPointer(0,2,b.FLOAT,!1,0,0);d=new Uint8Array(4);b.useProgram(e);b.viewport(0,0,1,1);b.bindTexture(b.TEXTURE_2D,c);b.drawArrays(b.POINTS,0,1);b.readPixels(0,0,1,1,b.RGBA,b.UNSIGNED_BYTE,d);return 127===d[0]||128===d[0]}function d(){}function c(a){"OES_texture_float_linear"===a?(void 0===this.$OES_texture_float_linear$&&Object.defineProperty(this,
"$OES_texture_float_linear$",{enumerable:!1,configurable:!1,writable:!1,value:new d}),a=this.$OES_texture_float_linear$):a=n.call(this,a);return a}function e(){var a=f.call(this);-1===a.indexOf("OES_texture_float_linear")&&a.push("OES_texture_float_linear");return a}try{var g=document.createElement("canvas").getContext("experimental-webgl")}catch(l){}if(g&&-1===g.getSupportedExtensions().indexOf("OES_texture_float_linear")&&a(g)){var n=WebGLRenderingContext.prototype.getExtension,f=WebGLRenderingContext.prototype.getSupportedExtensions;
WebGLRenderingContext.prototype.getExtension=c;WebGLRenderingContext.prototype.getSupportedExtensions=e}})();var a;v.canvas=function(){var b=document.createElement("canvas");try{a=b.getContext("experimental-webgl",{premultipliedAlpha:!1})}catch(d){a=null}if(!a)throw"This browser does not support WebGL";b._={gl:a,isInitialized:!1,texture:null,spareTexture:null,flippedShader:null};b.texture=k(A);b.draw=k(C);b.update=k(D);b.replace=k(E);b.contents=k(F);b.getPixelArray=k(G);b.brightnessContrast=k(H);
b.hexagonalPixelate=k(X);b.hueSaturation=k(K);b.colorHalftone=k(U);b.triangleBlur=k(S);b.unsharpMask=k(N);b.perspective=k(aa);b.matrixWarp=k($);b.bulgePinch=k(Z);b.tiltShift=k(R);b.dotScreen=k(V);b.edgeWork=k(W);b.lensBlur=k(Q);b.zoomBlur=k(T);b.noise=k(L);b.denoise=k(J);b.curves=k(I);b.swirl=k(ba);b.ink=k(Y);b.vignette=k(P);b.vibrance=k(O);b.sepia=k(M);return b};v.splineInterpolate=t;var h=function(){function b(b,c){var e=a.createShader(b);a.shaderSource(e,c);a.compileShader(e);if(!a.getShaderParameter(e,
a.COMPILE_STATUS))throw"compile error: "+a.getShaderInfoLog(e);return e}function d(d,l){this.texCoordAttribute=this.vertexAttribute=null;this.program=a.createProgram();d=d||c;l=l||e;l="precision highp float;"+l;a.attachShader(this.program,b(a.VERTEX_SHADER,d));a.attachShader(this.program,b(a.FRAGMENT_SHADER,l));a.linkProgram(this.program);if(!a.getProgramParameter(this.program,a.LINK_STATUS))throw"link error: "+a.getProgramInfoLog(this.program);}var c="attribute vec2 vertex;attribute vec2 _texCoord;varying vec2 texCoord;void main(){texCoord=_texCoord;gl_Position=vec4(vertex*2.0-1.0,0.0,1.0);}",
e="uniform sampler2D texture;varying vec2 texCoord;void main(){gl_FragColor=texture2D(texture,texCoord);}";d.prototype.destroy=function(){a.deleteProgram(this.program);this.program=null};d.prototype.uniforms=function(b){a.useProgram(this.program);for(var e in b)if(b.hasOwnProperty(e)){var c=a.getUniformLocation(this.program,e);if(null!==c){var d=b[e];if("[object Array]"==Object.prototype.toString.call(d))switch(d.length){case 1:a.uniform1fv(c,new Float32Array(d));break;
case 2:a.uniform2fv(c,new Float32Array(d));break;case 3:a.uniform3fv(c,new Float32Array(d));break;case 4:a.uniform4fv(c,new Float32Array(d));break;case 9:a.uniformMatrix3fv(c,!1,new Float32Array(d));break;case 16:a.uniformMatrix4fv(c,!1,new Float32Array(d));break;default:throw"dont't know how to load uniform \""+e+'" of length '+d.length;}else if("[object Number]"==Object.prototype.toString.call(d))a.uniform1f(c,d);else throw'attempted to set uniform "'+e+'" to invalid value '+(d||"undefined").toString();
}}return this};d.prototype.textures=function(b){a.useProgram(this.program);for(var c in b)b.hasOwnProperty(c)&&a.uniform1i(a.getUniformLocation(this.program,c),b[c]);return this};d.prototype.drawRect=function(b,c,e,d){var f=a.getParameter(a.VIEWPORT);c=void 0!==c?(c-f[1])/f[3]:0;b=void 0!==b?(b-f[0])/f[2]:0;e=void 0!==e?(e-f[0])/f[2]:1;d=void 0!==d?(d-f[1])/f[3]:1;null==a.vertexBuffer&&(a.vertexBuffer=a.createBuffer());a.bindBuffer(a.ARRAY_BUFFER,a.vertexBuffer);a.bufferData(a.ARRAY_BUFFER,new Float32Array([b,
c,b,d,e,c,e,d]),a.STATIC_DRAW);null==a.texCoordBuffer&&(a.texCoordBuffer=a.createBuffer(),a.bindBuffer(a.ARRAY_BUFFER,a.texCoordBuffer),a.bufferData(a.ARRAY_BUFFER,new Float32Array([0,0,0,1,1,0,1,1]),a.STATIC_DRAW));null==this.vertexAttribute&&(this.vertexAttribute=a.getAttribLocation(this.program,"vertex"),a.enableVertexAttribArray(this.vertexAttribute));null==this.texCoordAttribute&&(this.texCoordAttribute=a.getAttribLocation(this.program,"_texCoord"),a.enableVertexAttribArray(this.texCoordAttribute));
a.useProgram(this.program);a.bindBuffer(a.ARRAY_BUFFER,a.vertexBuffer);a.vertexAttribPointer(this.vertexAttribute,2,a.FLOAT,!1,0,0);a.bindBuffer(a.ARRAY_BUFFER,a.texCoordBuffer);a.vertexAttribPointer(this.texCoordAttribute,2,a.FLOAT,!1,0,0);a.drawArrays(a.TRIANGLE_STRIP,0,4)};d.getDefaultShader=function(){a.defaultShader=a.defaultShader||new d;return a.defaultShader};return d}();z.prototype.interpolate=function(a){for(var d=0,c=this.ya.length-1;1<c-d;){var e=c+d>>1;this.xa[e]>a?c=e:d=e}var e=this.xa[c]-
this.xa[d],g=(this.xa[c]-a)/e;a=(a-this.xa[d])/e;return g*this.ya[d]+a*this.ya[c]+((g*g*g-g)*this.y2[d]+(a*a*a-a)*this.y2[c])*e*e/6};var r=function(){function b(b,c,d,f){this.gl=a;this.id=a.createTexture();this.width=b;this.height=c;this.format=d;this.type=f;a.bindTexture(a.TEXTURE_2D,this.id);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_WRAP_S,a.CLAMP_TO_EDGE);a.texParameteri(a.TEXTURE_2D,
a.TEXTURE_WRAP_T,a.CLAMP_TO_EDGE);b&&c&&a.texImage2D(a.TEXTURE_2D,0,this.format,b,c,0,this.format,this.type,null)}function d(a){null==c&&(c=document.createElement("canvas"));c.width=a.width;c.height=a.height;a=c.getContext("2d");a.clearRect(0,0,c.width,c.height);return a}b.fromElement=function(c){var d=new b(0,0,a.RGBA,a.UNSIGNED_BYTE);d.loadContentsOf(c);return d};b.prototype.loadContentsOf=function(b){this.width=b.width||b.videoWidth;this.height=b.height||b.videoHeight;a.bindTexture(a.TEXTURE_2D,
this.id);a.texImage2D(a.TEXTURE_2D,0,this.format,this.format,this.type,b)};b.prototype.initFromBytes=function(b,c,d){this.width=b;this.height=c;this.format=a.RGBA;this.type=a.UNSIGNED_BYTE;a.bindTexture(a.TEXTURE_2D,this.id);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,b,c,0,a.RGBA,this.type,new Uint8Array(d))};b.prototype.destroy=function(){a.deleteTexture(this.id);this.id=null};b.prototype.use=function(b){a.activeTexture(a.TEXTURE0+(b||0));a.bindTexture(a.TEXTURE_2D,this.id)};b.prototype.unuse=function(b){a.activeTexture(a.TEXTURE0+
(b||0));a.bindTexture(a.TEXTURE_2D,null)};b.prototype.ensureFormat=function(b,c,d,f){if(1==arguments.length){var h=arguments[0];b=h.width;c=h.height;d=h.format;f=h.type}if(b!=this.width||c!=this.height||d!=this.format||f!=this.type)this.width=b,this.height=c,this.format=d,this.type=f,a.bindTexture(a.TEXTURE_2D,this.id),a.texImage2D(a.TEXTURE_2D,0,this.format,b,c,0,this.format,this.type,null)};b.prototype.drawTo=function(b){a.framebuffer=a.framebuffer||a.createFramebuffer();a.bindFramebuffer(a.FRAMEBUFFER,
a.framebuffer);a.framebufferTexture2D(a.FRAMEBUFFER,a.COLOR_ATTACHMENT0,a.TEXTURE_2D,this.id,0);if(a.checkFramebufferStatus(a.FRAMEBUFFER)!==a.FRAMEBUFFER_COMPLETE)throw Error("incomplete framebuffer");a.viewport(0,0,this.width,this.height);b();a.bindFramebuffer(a.FRAMEBUFFER,null)};var c=null;b.prototype.fillUsingCanvas=function(b){b(d(this));this.format=a.RGBA;this.type=a.UNSIGNED_BYTE;a.bindTexture(a.TEXTURE_2D,this.id);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,c);return this};
b.prototype.toImage=function(b){this.use();h.getDefaultShader().drawRect();var f=4*this.width*this.height,k=new Uint8Array(f),n=d(this),p=n.createImageData(this.width,this.height);a.readPixels(0,0,this.width,this.height,a.RGBA,a.UNSIGNED_BYTE,k);for(var m=0;m<f;m++)p.data[m]=k[m];n.putImageData(p,0,0);b.src=c.toDataURL()};b.prototype.swapWith=function(a){var b;b=a.id;a.id=this.id;this.id=b;b=a.width;a.width=this.width;this.width=b;b=a.height;a.height=this.height;this.height=b;b=a.format;a.format=
this.format;this.format=b};return b}(),s="float random(vec3 scale,float seed){return fract(sin(dot(gl_FragCoord.xyz+seed,scale))*43758.5453+seed);}";return v}();
export default fx;
+717
View File
@@ -0,0 +1,717 @@
import config from "../config";
/**
* various helpers
*
* @author ViliusL
*/
class Helper_class {
constructor() {
this.time = null;
}
get_url_parameters() {
var queryDict = {};
location.search.substr(1).split("&").forEach(
function (item) {
queryDict[item.split("=")[0]] = item.split("=")[1];
}
);
return queryDict;
}
/**
* starts timer
*/
timer_start() {
this.time = Date.now();
}
/**
* calculates time between two calls.
*
* @param {string} name Optional
* @param {boolean} echo Default is true.
*/
timer_end(name, echo) {
var text = (Math.round(Date.now() - this.time) / 1000) + " s";
if (echo != undefined && echo === false)
return text;
if (name != undefined)
text += ' (' + name + ')';
console.log(text);
}
//format time
format_time(datetime) {
return new Date(datetime).toJSON().slice(0, 19).replace(/T/g, ' ');
}
/**
* Find the position of the first occurrence of string or false.
*
* @param {string} haystack
* @param {string} needle
* @param {int} offset
* @returns {Boolean|String}
*/
strpos(haystack, needle, offset = 0) {
var i = (haystack + '').indexOf(needle, (offset || 0));
return i === -1 ? false : i;
}
/**
* return cookie value from global cookie
*
* @param {string} name
* @returns {object|string}
*/
getCookie(name) {
var cookie = this._getCookie('config');
if (cookie == '')
cookie = {};
else
cookie = JSON.parse(cookie);
if (cookie[name] != undefined)
return cookie[name];
else
return null;
}
/**
* sets cookie value to global cookie
*
* @param {string} name
* @param {string|number} value
*/
setCookie(name, value) {
var cookie = this._getCookie('config');
if (cookie == '')
cookie = {};
else
cookie = JSON.parse(cookie);
cookie[name] = value;
var cookie = JSON.stringify(cookie);
this._setCookie('config', cookie);
}
_getCookie(NameOfCookie) {
if (document.cookie.length > 0) {
var begin = document.cookie.indexOf(NameOfCookie + "=");
if (begin != -1) {
begin += NameOfCookie.length + 1;
var end = document.cookie.indexOf(";", begin);
if (end == -1)
end = document.cookie.length;
return document.cookie.substring(begin, end);
}
}
return '';
}
_setCookie(NameOfCookie, value, expire_days) {
if (expire_days == undefined)
expire_days = 180;
var ExpireDate = new Date();
ExpireDate.setTime(ExpireDate.getTime() + (expire_days * 24 * 3600 * 1000));
document.cookie = NameOfCookie + "=" + value +
((expire_days == null) ? "" : "; expires=" + ExpireDate.toGMTString());
}
delCookie(NameOfCookie) {
if (this.getCookie(NameOfCookie)) {
document.cookie = NameOfCookie + "=" +
"; expires=Thu, 01-Jan-70 00:00:01 GMT";
}
}
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
font_pixel_to_height(px) {
return Math.round(px * 0.75);
}
hex(x) {
x = parseInt(x);
return ("0" + x.toString(16)).slice(-2);
}
hex_set_hsl(hex, newHsl) {
const rgb = this.hexToRgb(hex);
const hsl = this.rgbToHsl(rgb.r, rgb.g, rgb.b);
if ('h' in newHsl) {
hsl.h = newHsl.h;
}
if ('s' in newHsl) {
hsl.s = newHsl.s;
}
if ('l' in newHsl) {
hsl.l = newHsl.l;
}
return this.hslToHex(hsl.h, hsl.s, hsl.l);
}
rgbToHex(r, g, b) {
if (r > 255 || g > 255 || b > 255)
throw "Invalid color component";
var tmp = ((r << 16) | (g << 8) | b).toString(16);
return "#" + ("000000" + tmp).slice(-6);
}
hexToRgb(hex) {
if (hex[0] == "#")
hex = hex.substr(1);
if (hex.length == 3) {
var temp = hex;
hex = '';
temp = /^([a-f0-9])([a-f0-9])([a-f0-9])$/i.exec(temp).slice(1);
for (var i = 0; i < 3; i++)
hex += temp[i] + temp[i];
}
var triplets = /^([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i.exec(hex).slice(1);
return {
r: parseInt(triplets[0], 16),
g: parseInt(triplets[1], 16),
b: parseInt(triplets[2], 16),
a: 255
};
}
hslToHex(h, s, l) {
const rgb = this.hslToRgb(h, s, l);
return this.rgbToHex(rgb.r, rgb.g, rgb.b);
}
hsvToHex(h, s, v) {
const rgb = this.hsvToRgb(h, s, v);
return this.rgbToHex(rgb.r, rgb.g, rgb.b);
}
hueToRgb(p, q, t) {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
/**
* Converts an HSL color value to RGB.
* Assumes h, s, and l are contained in the set [0, 1]
* Returns r, g, and b in the set [0, 255].
*
* Credit: https://gist.github.com/mjackson/5311256
*
* @param {number} h The hue
* @param {number} s The saturation
* @param {number} l The lightness
* @return {Object} The RGB representation, r,g,b as keys.
*/
hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
}
else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = this.hueToRgb(p, q, h + 1 / 3);
g = this.hueToRgb(p, q, h);
b = this.hueToRgb(p, q, h - 1 / 3);
}
return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) };
}
/**
* Converts an RGB color value to HSL. Values are in range 0-1.
* But real ranges are 0-360, 0-100%, 0-100%
*
* Credit: https://gist.github.com/mjackson/5311256
*
* @param {number} r red color value
* @param {number} g green color value
* @param {number} b blue color value
* @return {object} The HSL representation
*/
rgbToHsl(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
}
else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return { h, s, l };
}
/**
* Converts an RGB color value to HSV.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and v in the set [0, 1].
*
* Credit: https://gist.github.com/mjackson/5311256
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return {object} The HSL representation
*/
rgbToHsv(r, g, b) {
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, v = max;
var d = max - min;
s = max == 0 ? 0 : d / max;
if (max == min) {
h = 0; // achromatic
} else {
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h, s, v };
}
/**
* Converts an HSV color value to RGB.
* Assumes h, s, and v are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* Credit: https://gist.github.com/mjackson/5311256
*
* @param Number h The hue
* @param Number s The saturation
* @param Number v The value
* @return {object} The RGB representation
*/
hsvToRgb(h, s, v) {
var r, g, b;
var i = Math.floor(h * 6);
var f = h * 6 - i;
var p = v * (1 - s);
var q = v * (1 - f * s);
var t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return { r: r * 255, g: g * 255, b: b * 255 };
}
/**
* Converts an HSV color value to HSL.
* Assumes h, s, and v are contained in the set [0, 1] and
* returns h, s, and l in the set [0, 1].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number v The value
* @return {object} The HSL representation
*/
hsvToHsl(h, s, v) {
return {
h,
s: s * v / Math.max(0.00000001, ((h = (2 - s) * v) < 1 ? h : 2 - h)),
l: h / 2
};
}
/**
* Converts an HSL color value to HSV.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns h, s, and v in the set [0, 1].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The value
* @return {object} The HSV representation
*/
hslToHsv(h, s, l) {
s *= l < .5 ? l : 1 - l;
return {
h,
s: 2 * s / Math.max(0.00000001, (l + s)),
v: l + s
};
}
remove_selection() {
if (window.getSelection) {
if (window.getSelection().empty) // Chrome
window.getSelection().empty();
else if (window.getSelection().removeAllRanges) // Firefox
window.getSelection().removeAllRanges();
}
else if (document.selection) // IE?
document.selection.empty();
}
//credits: richard maloney 2006
darkenColor(color, v) {
if (color.length > 6) {
color = color.substring(1, color.length);
}
var rgb = parseInt(color, 16);
var r = Math.abs(((rgb >> 16) & 0xFF) + v);
if (r > 255)
r = r - (r - 255);
var g = Math.abs(((rgb >> 8) & 0xFF) + v);
if (g > 255)
g = g - (g - 255);
var b = Math.abs((rgb & 0xFF) + v);
if (b > 255)
b = b - (b - 255);
r = Number(r < 0 || isNaN(r)) ? 0 : ((r > 255) ? 255 : r).toString(16);
if (r.length == 1)
r = '0' + r;
g = Number(g < 0 || isNaN(g)) ? 0 : ((g > 255) ? 255 : g).toString(16);
if (g.length == 1)
g = '0' + g;
b = Number(b < 0 || isNaN(b)) ? 0 : ((b > 255) ? 255 : b).toString(16);
if (b.length == 1)
b = '0' + b;
return "#" + r + g + b;
}
/**
* JavaScript Number Formatter, author: KPL, KHL
*
* @param {int} n
* @param {int} maximumFractionDigits
* @returns {string}
*/
number_format(n, maximumFractionDigits) {
let x = parseFloat(n);
var number = x.toLocaleString('us', {minimumFractionDigits: 0, maximumFractionDigits: maximumFractionDigits});
number = number.replaceAll(',', '');
number = parseFloat(number);
return number;
}
check_input_color_support() {
var i = document.createElement("input");
i.setAttribute("type", "color");
return i.type !== "text";
}
b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
escapeHtml(text) {
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
ucfirst(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
/**
* change canvas size without loosing data
*
* @param {canvas} canvas
* @param {int} width
* @param {int} height
* @param {int} offset_x
* @param {int} offset_y
*/
change_canvas_size(canvas, width, height, offset_x, offset_y) {
if (offset_x == undefined)
offset_x = 0;
if (offset_y == undefined)
offset_y = 0;
//copy data;
var tmp = document.createElement('canvas');
var ctx = tmp.getContext("2d");
tmp.width = canvas.width;
tmp.height = canvas.height;
ctx.drawImage(canvas, 0, 0);
canvas.width = Math.max(1, width);
canvas.height = Math.max(1, height);
//restore image
canvas.getContext("2d").drawImage(tmp, -offset_x, -offset_y);
}
image_round(ctx_main, mouse_x, mouse_y, size_w, size_h, img_data, anti_aliasing = false) {
//create tmp canvas
var canvasTmp = document.createElement('canvas');
canvasTmp.width = size_w;
canvasTmp.height = size_h;
var size_half_w = Math.round(size_w / 2);
var size_half_h = Math.round(size_h / 2);
var ctx = canvasTmp.getContext("2d");
var width = canvasTmp.width;
var height = canvasTmp.height;
var xx = mouse_x - size_half_w;
var yy = mouse_y - size_half_h;
ctx.clearRect(0, 0, width, height);
ctx.save();
//draw main data
ctx.putImageData(img_data, 0, 0);
ctx.globalCompositeOperation = 'destination-in';
//create form
var gradient = ctx.createRadialGradient(size_half_w, size_half_h, 0, size_half_w, size_half_h, size_half_w);
gradient.addColorStop(0, '#ffffff');
if (anti_aliasing == true)
gradient.addColorStop(0.8, '#ffffff');
else
gradient.addColorStop(0.99, '#ffffff');
gradient.addColorStop(1, 'rgba(255,255,255,0');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.ellipse(size_half_w, size_half_h, size_w * 2, size_h * 2, 0, 0, 2 * Math.PI);
ctx.fill();
ctx_main.drawImage(canvasTmp, 0, 0, size_w, size_h, xx, yy, size_w, size_h);
//reset
ctx.restore();
ctx.clearRect(0, 0, width, height);
}
is_input(element) {
if (!element) {
return false;
}
if (element.type == 'text' || element.tagName == 'INPUT' || element.type == 'textarea') {
return true;
} else {
return element.closest('.ui_color_picker_gradient, .ui_number_input, .ui_range, .ui_swatches') != null;
}
}
//if IE 11 or Edge
is_edge_or_ie() {
//ie11
if( !(window.ActiveXObject) && "ActiveXObject" in window )
return true;
//edge
if( navigator.userAgent.indexOf('Edge/') != -1 )
return true;
return false;
}
// Credit: https://stackoverflow.com/questions/27078285/simple-throttle-in-js
throttle(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
/**
* draws line that is visible on white and black backgrounds.
*
* @param ctx
* @param start_x
* @param start_y
* @param end_x
* @param end_y
*/
draw_special_line(ctx, start_x, start_y, end_x, end_y){
const wholeLineWidth = 2 / config.ZOOM;
const halfLineWidth = wholeLineWidth / 2;
ctx.lineWidth = wholeLineWidth;
ctx.strokeStyle = 'rgb(255, 255, 255)';
ctx.beginPath();
ctx.moveTo(start_x - halfLineWidth, start_y);
ctx.lineTo(end_x - halfLineWidth, end_y);
ctx.stroke();
ctx.lineWidth = halfLineWidth;
ctx.strokeStyle = 'rgb(0, 0, 0)';
ctx.beginPath();
ctx.moveTo(start_x - halfLineWidth, start_y);
ctx.lineTo(end_x - halfLineWidth, end_y);
ctx.stroke();
}
/**
* draws control point that is visible on white and black backgrounds.
*
* @param ctx
* @param x
* @param y
* @returns {Path2D}
*/
draw_control_point(ctx, x, y) {
var dx = 0;
var dy = 0;
var block_size = 12 / config.ZOOM;
const wholeLineWidth = 2 / config.ZOOM;
ctx.strokeStyle = "#000000";
ctx.fillStyle = "#ffffff";
ctx.lineWidth = wholeLineWidth;
//create path
const circle = new Path2D();
circle.arc(x + dx * block_size, y + dy * block_size, block_size / 2, 0, 2 * Math.PI);
//draw
ctx.fill(circle);
ctx.stroke(circle);
return circle;
}
/**
* converts internal unit (pixel) to user defined
*
* @param data
* @param type
* @param resolution
* @returns {string|number}
*/
get_user_unit(data, type, resolution){
data = parseFloat(data);
if(type == 'pixels'){
//no conversion
return parseInt(data);
}
else if(type == 'inches'){
return this.number_format(data / resolution, 3);
}
else if(type == 'centimeters'){
return this.number_format(data / resolution * 2.54, 3);
}
else if(type == 'millimetres'){
return this.number_format(data / resolution * 25.4, 3);
}
}
/**
* converts user defined unit to internal (pixels)
*
* @param data
* @param type
* @param resolution
* @returns {number}
*/
get_internal_unit(data, type, resolution){
data = parseFloat(data);
if(type == 'pixels'){
//no conversion
return parseInt(data);
}
else if(type == 'inches'){
return Math.ceil(data * resolution);
}
else if(type == 'centimeters'){
return Math.ceil(data * resolution / 2.54);
}
else if(type == 'millimetres'){
return Math.ceil(data * resolution / 25.4);
}
}
}
export default Helper_class;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,65 @@
// https://github.com/jorgejeferson/translate.js/tree/39be8237666a76035fc210a28d8e431f1416579e
(function ($) {
$.fn.translate = function (options) {
var that = this; //a reference to ourselves
var settings = {
css: "trn",
attrs: ["alt", "placeholder", "title"],
lang: "pt",
langDefault: "pt",
};
settings = $.extend(settings, options || {});
if (settings.css.lastIndexOf(".", 0) !== 0) { //doesn't start with '.'
settings.css = "." + settings.css;
}
var t = settings.t;
//public methods
this.lang = function (l) {
if (l) {
settings.lang = l;
this.translate(settings); //translate everything
}
return settings.lang;
};
this.get = function (index) {
var res = index;
try {
res = t[index][settings.lang];
}
catch (err) { //not found, return index
return index;
}
if (res) {
return res;
}
else {
return index;
}
};
this.g = this.get;
//main
this.find(settings.css).each(function (i) {
var $this = $(this);
var trn_key = $this.attr("data-trn-key");
if (!trn_key) {
trn_key = $this.html();
$this.attr("data-trn-key", trn_key);
}
// Filtering attr
$.each(this.attributes, function () {
if ($.inArray(this.name, settings.attrs) !== -1) {
var trn_attr_key = $this.attr("data-trn-attr");
if (!trn_attr_key) {
trn_attr_key = $this.attr(this.name);
$this.attr("data-trn-attr", trn_attr_key);
}
$this.attr(this.name, that.get(trn_attr_key));
}
});
$this.html(that.get(trn_key));
});
return this;
};
})(jQuery);
@@ -0,0 +1,240 @@
/**
* Minimal pure-JS PDF writer for image export.
*
* Supports:
* - Single-page and multipage (one page per canvas)
* - RGB color space: images JPEG-encoded (browser-native, small files)
* - CMYK color space: raw DeviceCMYK bytes (print-ready, no alpha)
*
* PDF-1.4 structure used. No external dependencies.
*
* Usage:
* PdfWriter.fromCanvases(canvases, { colorMode: 'rgb'|'cmyk', quality: 0.9, dpi: 300 })
* .then(blob => FileSaver.saveAs(blob, 'file.pdf'));
*/
import { rgbToCmyk } from './tiff-writer.js';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Encode a JS string to a Uint8Array of bytes (Latin-1 safe). */
function strBytes(s) {
var a = new Uint8Array(s.length);
for (var i = 0; i < s.length; i++) a[i] = s.charCodeAt(i) & 0xff;
return a;
}
/** Concatenate multiple Uint8Arrays / ArrayBuffers into one Uint8Array. */
function concat(parts) {
var total = 0;
for (var i = 0; i < parts.length; i++)
total += parts[i].byteLength || parts[i].length;
var out = new Uint8Array(total), pos = 0;
for (var i = 0; i < parts.length; i++) {
var p = parts[i] instanceof ArrayBuffer ? new Uint8Array(parts[i]) : parts[i];
out.set(p, pos);
pos += p.length;
}
return out;
}
/** canvas → raw CMYK Uint8Array (W*H*4 bytes, alpha discarded). */
function canvasToCmykBytes(canvas) {
var idata = canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height).data;
var out = new Uint8Array(canvas.width * canvas.height * 4);
for (var px = 0, i = 0, len = idata.length; px < len; px += 4, i += 4) {
var cmyk = rgbToCmyk(idata[px], idata[px + 1], idata[px + 2]);
out[i] = cmyk[0];
out[i + 1] = cmyk[1];
out[i + 2] = cmyk[2];
out[i + 3] = cmyk[3];
}
return out;
}
/** canvas → JPEG Uint8Array via browser encoding. Returns a Promise. */
function canvasToJpegBytes(canvas, quality) {
return new Promise(function(resolve) {
canvas.toBlob(function(blob) {
blob.arrayBuffer().then(function(buf) {
resolve(new Uint8Array(buf));
});
}, 'image/jpeg', quality || 0.92);
});
}
// ---------------------------------------------------------------------------
// PDF object builder
// ---------------------------------------------------------------------------
/**
* Build a complete PDF byte stream for an array of pages.
*
* @param {Array<{width, height, colorSpace, imgBytes, filter}>} pages
* @param {number} dpi — used for MediaBox sizing (px → pt: pt = px * 72 / dpi)
* @returns {Uint8Array}
*/
function buildPDF(pages, dpi) {
dpi = dpi || 300;
var px2pt = 72 / dpi;
// Object registry: we'll collect byte-offset of each object for xref.
var objs = []; // each element is the raw bytes of "N 0 obj ... endobj\n"
var objNums = {}; // logical name → 1-based index
function addObj(name, content) {
var n = objs.length + 1;
if (name) objNums[name] = n;
var s = n + ' 0 obj\n' + content + '\nendobj\n';
objs.push(strBytes(s));
return n;
}
function addStreamObj(name, dict, dataBytes) {
var n = objs.length + 1;
if (name) objNums[name] = n;
var header = n + ' 0 obj\n' + dict + '\nstream\n';
var footer = '\nendstream\nendobj\n';
var combined = concat([strBytes(header), dataBytes, strBytes(footer)]);
objs.push(combined);
return n;
}
// 1. Catalog
addObj('catalog', '<< /Type /Catalog /Pages 2 0 R >>');
// 2. Pages (placeholder — children added later)
var pagesIdx = objs.length + 1;
addObj('pages', ''); // placeholder
// 3. Per-page objects
var pageObjNums = [];
for (var p = 0; p < pages.length; p++) {
var pg = pages[p];
var W_pt = (pg.width * px2pt).toFixed(3);
var H_pt = (pg.height * px2pt).toFixed(3);
var imgName = 'Im' + (p + 1);
var imgIdx = objs.length + 2; // will be added after content stream
// Content stream: scale and paint image
var contentStr = 'q ' + W_pt + ' 0 0 ' + H_pt + ' 0 0 cm /' + imgName + ' Do Q';
var contentNum = addStreamObj(null,
'<< /Length ' + contentStr.length + ' >>',
strBytes(contentStr));
// Image XObject
var samples = pg.colorSpace === 'DeviceCMYK' ? 4 : 3;
var imgDict = '<< /Type /XObject /Subtype /Image'
+ ' /Width ' + pg.width
+ ' /Height ' + pg.height
+ ' /ColorSpace /' + pg.colorSpace
+ ' /BitsPerComponent 8'
+ (pg.filter ? ' /Filter /' + pg.filter : '')
+ ' /Length ' + pg.imgBytes.length
+ ' >>';
var imgNum = addStreamObj(null, imgDict, pg.imgBytes);
// Page object
var pageNum = addObj(null,
'<< /Type /Page /Parent ' + pagesIdx + ' 0 R'
+ ' /MediaBox [0 0 ' + W_pt + ' ' + H_pt + ']'
+ ' /Contents ' + contentNum + ' 0 R'
+ ' /Resources << /XObject << /' + imgName + ' ' + imgNum + ' 0 R >> >>'
+ ' >>');
pageObjNums.push(pageNum);
}
// Fill in Pages object properly
var kidsStr = pageObjNums.map(function(n) { return n + ' 0 R'; }).join(' ');
var pagesContent = '<< /Type /Pages /Kids [' + kidsStr + '] /Count ' + pages.length + ' >>';
objs[pagesIdx - 1] = strBytes(pagesIdx + ' 0 obj\n' + pagesContent + '\nendobj\n');
// ---- Assemble file ----
var header = strBytes('%PDF-1.4\n%\xE2\xE3\xCF\xD3\n'); // binary hint comment
var offsets = [];
var parts = [header];
var bytePos = header.length;
for (var i = 0; i < objs.length; i++) {
offsets.push(bytePos);
parts.push(objs[i]);
bytePos += objs[i].length;
}
// xref table
var xrefOffset = bytePos;
var xrefLines = 'xref\n0 ' + (objs.length + 1) + '\n';
xrefLines += '0000000000 65535 f \n';
for (var i = 0; i < offsets.length; i++) {
xrefLines += String(offsets[i]).padStart(10, '0') + ' 00000 n \n';
}
parts.push(strBytes(xrefLines));
// trailer
var trailerStr = 'trailer\n<< /Size ' + (objs.length + 1)
+ ' /Root 1 0 R >>\nstartxref\n' + xrefOffset + '\n%%EOF\n';
parts.push(strBytes(trailerStr));
return concat(parts);
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
var PdfWriter = {
/**
* Export an array of canvases as a PDF.
*
* @param {HTMLCanvasElement|HTMLCanvasElement[]} canvases
* @param {object} [opts]
* @param {'rgb'|'cmyk'} [opts.colorMode='rgb']
* @param {number} [opts.quality=0.92] JPEG quality for RGB mode (01)
* @param {number} [opts.dpi=300] dots-per-inch for page sizing
* @returns {Promise<Blob>}
*/
fromCanvases: function(canvases, opts) {
if (!Array.isArray(canvases)) canvases = [canvases];
opts = opts || {};
var colorMode = opts.colorMode === 'cmyk' ? 'cmyk' : 'rgb';
var dpi = +(opts.dpi || 300) | 0;
var quality = opts.quality != null ? opts.quality : 0.92;
if (colorMode === 'cmyk') {
// Synchronous path: raw CMYK bytes
var pages = canvases.map(function(cv) {
return {
width: cv.width,
height: cv.height,
colorSpace: 'DeviceCMYK',
filter: null,
imgBytes: canvasToCmykBytes(cv),
};
});
var pdfBytes = buildPDF(pages, dpi);
return Promise.resolve(new Blob([pdfBytes], { type: 'application/pdf' }));
} else {
// Async path: JPEG-encode each canvas
var promises = canvases.map(function(cv) {
return canvasToJpegBytes(cv, quality).then(function(bytes) {
return {
width: cv.width,
height: cv.height,
colorSpace: 'DeviceRGB',
filter: 'DCTDecode',
imgBytes: bytes,
};
});
});
return Promise.all(promises).then(function(pages) {
var pdfBytes = buildPDF(pages, dpi);
return new Blob([pdfBytes], { type: 'application/pdf' });
});
}
},
};
export default PdfWriter;
+676
View File
@@ -0,0 +1,676 @@
/**
* user dialogs library
*
* @author ViliusL
*
* Usage:
*
* import Dialog_class from './libs/popup.js';
* var POP = new popup();
*
* var settings = {
* title: 'Differences',
* comment: '',
* preview: true,
* className: '',
* params: [
* {name: "param1", title: "Parameter #1:", value: "111"},
* {name: "param2", title: "Parameter #2:", value: "222"},
* ],
* on_load: function(params){...},
* on_change: function(params, canvas_preview, w, h){...},
* on_finish: function(params){...},
* on_cancel: function(params){...},
* };
* this.POP.show(settings);
*
* Params types:
* - name type example
* - ---------------------------------------------------------------
* - name string 'parameter1'
* - title string 'enter value:'
* - type string 'select', 'textarea', 'color'
* - value string '314'
* - values array fo strings ['one', 'two', 'three']
* - range numbers interval [0, 255]
* - step int/float 1
* - placeholder text 'enter number here'
* - html html text '<b>bold</b>'
* - function function 'custom_function'
*/
import './../../css/popup.css';
import Base_layers_class from './../core/base-layers.js';
import Base_gui_class from './../core/base-gui.js';
import Tools_translate_class from './../modules/tools/translate.js';
var template = `
<button type="button" class="close" data-id="popup_close" title="Close">&times;</button>
<div data-id="pretitle_area"></div>
<span class="text_muted right" data-id="popup_comment"></span>
<h2 class="trn" data-id="popup_title"></h2>
<div class="dialog_content" data-id="dialog_content">
<div data-id="preview_content"></div>
<div data-id="params_content"></div>
</div>
<div class="buttons">
<button type="button" data-id="popup_ok" class="button trn">Ok</button>
<button type="button" data-id="popup_cancel" class="button trn">Cancel</button>
</div>
`;
class Dialog_class {
constructor() {
if (!window.POP) {
window.POP = this;
}
this.previousPOP = null;
this.el = null;
this.eventHandles = [];
this.active = false;
this.title = null;
this.onfinish = false;
this.oncancel = false;
this.preview = false;
this.preview_padding = 0;
this.onload = false;
this.onchange = false;
this.width_mini = 225;
this.height_mini = 200;
this.id = 0;
this.parameters = [];
this.Base_layers = new Base_layers_class();
this.Base_gui = new Base_gui_class();
this.Tools_translate = new Tools_translate_class();
this.last_params_hash = '';
this.layer_active_small = document.createElement("canvas");
this.layer_active_small_ctx = this.layer_active_small.getContext("2d");
this.caller = null;
this.resize_clicked = {x: null, y: null}
this.element_offset = {x: null, y: null}
}
/**
* shows dialog
*
* @param {array} config
*/
show(config) {
this.previousPOP = window.POP;
window.POP = this;
if (this.active == true) {
this.hide();
}
this.title = config.title || '';
this.parameters = config.params || [];
this.onfinish = config.on_finish || false;
this.oncancel = config.on_cancel || false;
this.preview = config.preview || false;
this.preview_padding = config.preview_padding || 0;
this.onchange = config.on_change || false;
this.onload = config.on_load || false;
this.className = config.className || '';
this.comment = config.comment || '';
//reset position
this.el = document.createElement('div');
this.el.classList = 'popup';
this.el.role = 'dialog';
document.querySelector('#popups').appendChild(this.el);
this.el.style.top = null;
this.el.style.left = null;
this.show_action();
this.set_events();
}
/**
* hides dialog
*
* @param {boolean} success
* @returns {undefined}
*/
hide(success) {
window.POP = this.previousPOP;
var params = this.get_params();
if (success === false && this.oncancel) {
this.oncancel(params);
}
if (this.el && this.el.parentNode) {
this.el.parentNode.removeChild(this.el);
}
this.parameters = [];
this.active = false;
this.preview = false;
this.preview_padding = 0;
this.onload = false;
this.onchange = false;
this.title = null;
this.className = '';
this.comment = '';
this.onfinish = false;
this.oncancel = false;
this.remove_events();
}
get_active_instances() {
return document.getElementById('popups').children.length;
}
/* ----------------- private functions ---------------------------------- */
addEventListener(target, type, listener, options) {
target.addEventListener(type, listener, options);
const handle = {
target, type, listener,
remove() {
target.removeEventListener(type, listener);
}
};
this.eventHandles.push(handle);
}
set_events() {
this.addEventListener(document, 'keydown', (event) => {
var code = event.code;
if (code == "Escape") {
//escape
this.hide(false);
}
}, false);
//register events
this.addEventListener(document, 'mousedown', (event) => {
if(event.target != this.el.querySelector('h2'))
return;
event.preventDefault();
this.resize_clicked.x = event.pageX;
this.resize_clicked.y = event.pageY;
var target = this.el;
this.element_offset.x = target.offsetLeft;
this.element_offset.y = target.offsetTop;
}, false);
this.addEventListener(document, 'mousemove', (event) => {
if(this.resize_clicked.x != null){
var dx = this.resize_clicked.x - event.pageX;
var dy = this.resize_clicked.y - event.pageY;
var target = this.el;
target.style.left = (this.element_offset.x - dx) + "px";
target.style.top = (this.element_offset.y - dy) + "px";
}
}, false);
this.addEventListener(document, 'mouseup', (event) => {
if(event.target != this.el.querySelector('h2'))
return;
event.preventDefault();
this.resize_clicked.x = null;
this.resize_clicked.y = null;
}, false);
this.addEventListener(window, 'resize', (event) => {
var target = this.el;
target.style.top = null;
target.style.left = null;
}, false);
}
remove_events() {
for (let handle of this.eventHandles) {
handle.remove();
}
this.eventHandles = [];
}
onChangeEvent(e) {
var params = this.get_params();
var hash = JSON.stringify(params);
if (this.last_params_hash == hash && this.onchange == false) {
//nothing changed
return;
}
this.last_params_hash = hash;
if (this.onchange != false) {
if (this.preview != false) {
var canvas_right = this.el.querySelector('[data-id="pop_post"]');
var ctx_right = canvas_right.getContext("2d");
ctx_right.clearRect(0, 0, this.width_mini, this.height_mini);
ctx_right.drawImage(this.layer_active_small,
this.preview_padding, this.preview_padding,
this.width_mini - this.preview_padding * 2, this.height_mini - this.preview_padding * 2
);
this.onchange(params, ctx_right, this.width_mini, this.height_mini, canvas_right);
}
else {
this.onchange(params);
}
}
}
//renders preview. If input=range supported, is called on every param update - must be fast...
preview_handler(e) {
if (this.preview !== false) {
this.onChangeEvent(e);
}
}
//OK pressed - prepare data and call handlers
save() {
var params = this.get_params();
if (this.onfinish) {
this.onfinish(params);
}
this.hide(true);
}
//"Cancel" pressed
cancel() {
if (this.oncancel) {
var params = this.get_params();
this.oncancel(params);
}
}
get_params() {
var response = {};
if(this.el == undefined){
return null;
}
var inputs = this.el.querySelectorAll('input');
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].id.substr(0, 9) == 'pop_data_') {
var key = inputs[i].id.substr(9);
if (this.strpos(key, "_poptmp") != false)
key = key.substring(0, this.strpos(key, "_poptmp"));
var value = inputs[i].value;
if (inputs[i].type == 'radio') {
if (inputs[i].checked == true)
response[key] = value;
}
else if (inputs[i].type == 'number') {
response[key] = parseFloat(value);
}
else if (inputs[i].type == 'checkbox') {
if (inputs[i].checked == true)
response[key] = true;
else
response[key] = false;
}
else if (inputs[i].type == 'range') {
response[key] = parseFloat(value);
}
else {
response[key] = value;
}
}
}
var selects = this.el.querySelectorAll('select');
for (var i = 0; i < selects.length; i++) {
if (selects[i].id.substr(0, 9) == 'pop_data_') {
var key = selects[i].id.substr(9);
response[key] = selects[i].value;
}
}
var textareas = this.el.querySelectorAll('textarea');
for (var i = 0; i < textareas.length; i++) {
if (textareas[i].id.substr(0, 9) == 'pop_data_') {
var key = textareas[i].id.substr(9);
response[key] = textareas[i].value;
}
}
return response;
}
/**
* show popup window.
* used strings: "Ok", "Cancel", "Preview"
*/
show_action() {
this.id = this.getRandomInt(0, 999999999);
if (this.active == true) {
this.hide();
return false;
}
this.active = true;
//build content
var html_pretitle_area = '';
var html_preview_content = '';
var html_params = '';
//preview area
if (this.preview !== false) {
html_preview_content += '<div class="preview_container">';
html_preview_content += '<canvas class="preview_canvas_left" width="' + this.width_mini + '" height="'
+ this.height_mini + '" data-id="pop_pre"></canvas>';
html_preview_content += '<div class="canvas_preview_container">';
html_preview_content += ' <canvas class="preview_canvas_post_back" width="' + this.width_mini
+ '" height="' + this.height_mini + '" data-id="pop_post_back"></canvas>';
html_preview_content += ' <canvas class="preview_canvas_post" width="' + this.width_mini + '" height="'
+ this.height_mini + '" data-id="pop_post"></canvas>';
html_preview_content += '</div>';
html_preview_content += '</div>';
}
//generate params
html_params += this.generateParamsHtml();
this.el.innerHTML = template;
this.el.querySelector('[data-id="pretitle_area"]').innerHTML = html_pretitle_area;
this.el.querySelector('[data-id="popup_title"]').innerHTML = this.title;
this.el.querySelector('[data-id="popup_comment"]').innerHTML = this.comment;
this.el.querySelector('[data-id="preview_content"]').innerHTML = html_preview_content;
this.el.querySelector('[data-id="params_content"]').innerHTML = html_params;
if (this.onfinish != false) {
this.el.querySelector('[data-id="popup_cancel"]').style.display = '';
}
else {
this.el.querySelector('[data-id="popup_cancel"]').style.display = 'none';
}
this.el.style.display = "block";
if (this.className) {
this.el.classList.add(this.className);
}
//replace color inputs
this.el.querySelectorAll('input[type="color"]').forEach((colorInput) => {
const id = colorInput.getAttribute('id');
colorInput.removeAttribute('id');
$(colorInput)
.uiColorInput({ inputId: id })
.on('change', (e) => {
this.onChangeEvent(e);
});
});
//events
this.el.querySelector('[data-id="popup_ok"]').addEventListener('click', (event) => {
this.save();
});
this.el.querySelector('[data-id="popup_cancel"]').addEventListener('click', (event) => {
this.hide(false);
});
this.el.querySelector('[data-id="popup_close"]').addEventListener('click', (event) => {
this.hide(false);
});
var targets = this.el.querySelectorAll('input');
for (var i = 0; i < targets.length; i++) {
targets[i].addEventListener('keyup', (event) => {
this.onkeyup(event);
});
}
//onload
if (this.onload) {
var params = this.get_params();
this.onload(params, this);
}
//load preview
if (this.preview !== false) {
//get canvas from layer
var canvas = this.Base_layers.convert_layer_to_canvas();
//draw original image
var canvas_left = this.el.querySelector('[data-id="pop_pre"]');
var pop_pre = canvas_left.getContext("2d");
pop_pre.clearRect(0, 0, this.width_mini, this.height_mini);
pop_pre.rect(0, 0, this.width_mini, this.height_mini);
pop_pre.fillStyle = "#ffffff";
pop_pre.fill();
this.draw_background(pop_pre, this.width_mini, this.height_mini, 10);
pop_pre.scale(this.width_mini / canvas.width, this.height_mini / canvas.height);
pop_pre.drawImage(canvas, 0, 0);
pop_pre.scale(1, 1);
//prepare temp canvas for faster repaint
this.layer_active_small.width = POP.width_mini;
this.layer_active_small.height = POP.height_mini;
this.layer_active_small_ctx.scale(this.width_mini / canvas.width, this.height_mini / canvas.height);
this.layer_active_small_ctx.drawImage(canvas, 0, 0);
this.layer_active_small_ctx.scale(1, 1);
//draw right background
var canvas_right_back = this.el.querySelector('[data-id="pop_post_back"]').getContext("2d");
this.draw_background(canvas_right_back, this.width_mini, this.height_mini, 10);
//copy to right side
var canvas_right = this.el.querySelector('[data-id="pop_post"]').getContext("2d");
canvas_right.clearRect(0, 0, this.width_mini, this.height_mini);
canvas_right.drawImage(canvas_left,
this.preview_padding, this.preview_padding,
this.width_mini - this.preview_padding * 2, this.height_mini - this.preview_padding * 2);
//prepare temp canvas
this.preview_handler();
}
//call translation again to translate popup
var lang = this.Base_gui.get_language();
this.Tools_translate.translate(lang);
}
generateParamsHtml() {
var html = '<table>';
var title = null;
for (var i in this.parameters) {
var parameter = this.parameters[i];
html += '<tr id="popup-tr-' + this.parameters[i].name + '">';
if (title != 'Error' && parameter.title != undefined)
html += '<th class="trn">' + parameter.title + '</th>';
if (parameter.name != undefined) {
if (parameter.values != undefined) {
if (parameter.values.length > 10 || parameter.type == 'select') {
//drop down
html += '<td colspan="2"><select onchange="POP.onChangeEvent();" id="pop_data_' + parameter.name
+ '">';
var k = 0;
for (var j in parameter.values) {
var sel = '';
if (parameter.value == parameter.values[j])
sel = 'selected="selected"';
if (parameter.value == undefined && k == 0)
sel = 'selected="selected"';
html += '<option ' + sel + ' name="' + parameter.values[j] + '">' + parameter.values[j]
+ '</option>';
k++;
}
html += '</select></td>';
}
else {
//radio
html += '<td class="radios" colspan="2">';
if (parameter.values.length > 2)
html += '<div class="group" id="popup-group-' + this.parameters[i].name + '">';
var k = 0;
for (var j in parameter.values) {
var ch = '';
if (parameter.value == parameter.values[j])
ch = 'checked="checked"';
if (parameter.value == undefined && k == 0)
ch = 'checked="checked"';
var title = parameter.values[j];
var parts = parameter.values[j].split(" - ");
if (parts.length > 1) {
title = parts[0] + ' - <span class="trn">' + parts[1] + '</span>';
}
html += '<input type="radio" onchange="POP.onChangeEvent();" ' + ch + ' name="'
+ parameter.name + '" id="pop_data_' + parameter.name + "_poptmp" + j + '" value="'
+ parameter.values[j] + '">';
html += '<label class="trn" for="pop_data_' + parameter.name + "_poptmp" + j + '">' + title
+ '</label>';
if (parameter.values.length > 2)
html += '<br />';
k++;
}
if (parameter.values.length > 2)
html += '</div>';
html += '</td>';
}
}
else if (parameter.value != undefined) {
//input, range, textarea, color
var step = 1;
if (parameter.step != undefined)
step = parameter.step;
if (parameter.range != undefined) {
//range
html += '<td><input type="range" name="' + parameter.name + '" id="pop_data_' + parameter.name
+ '" value="' + parameter.value + '" min="' + parameter.range[0] + '" max="'
+ parameter.range[1] + '" step="' + step
+ '" oninput="document.getElementById(\'pv' + i + '\').innerHTML = '
+ 'Math.round(this.value*100) / 100;POP.preview_handler();" '
+'onchange="POP.onChangeEvent();" /></td>';
html += '<td class="range_value" id="pv' + i + '">' + parameter.value + '</td>';
}
else if (parameter.type == 'color') {
//color
html += '<td><input type="color" id="pop_data_' + parameter.name + '" value="' + parameter.value
+ '" onchange="POP.onChangeEvent();" /></td>';
}
else if (typeof parameter.value == 'boolean') {
var checked = '';
if (parameter.value === true)
checked = 'checked';
html += '<td class="checkbox"><input type="checkbox" id="pop_data_' + parameter.name + '" '
+ checked + ' onclick="POP.onChangeEvent();" > <label class="trn" for="pop_data_'
+ parameter.name + '">Toggle</label></td>';
}
else {
//input or textarea
if (parameter.placeholder == undefined)
parameter.placeholder = '';
if (parameter.type == 'textarea') {
//textarea
html += '<td><textarea rows="10" id="pop_data_' + parameter.name
+ '" onchange="POP.onChangeEvent();" placeholder="' + parameter.placeholder + '" ' + (parameter.prevent_submission ? 'data-prevent-submission=""' : '' ) + '>'
+ parameter.value + '</textarea></td>';
}
else {
//text or number
var input_type = "text";
if (parameter.placeholder != '' && !isNaN(parameter.placeholder))
input_type = 'number';
if (parameter.value != undefined && typeof parameter.value == 'number')
input_type = 'number';
var comment_html = '';
if (typeof parameter.comment !== 'undefined') {
comment_html = '<span class="field_comment trn">' + parameter.comment + '</span>';
}
html += '<td colspan="2"><input type="' + input_type + '" id="pop_data_' + parameter.name
+ '" onchange="POP.onChangeEvent();" value="' + parameter.value + '" placeholder="'
+ parameter.placeholder + '" ' + (parameter.prevent_submission ? 'data-prevent-submission=""' : '' ) + ' />'+comment_html+'</td>';
}
}
}
}
else if (parameter.function != undefined) {
//custom function
var result;
result = parameter.function();
html += '<td colspan="3">' + result + '</td>';
}
else if (parameter.html != undefined) {
//html
html += '<td class="html_value" colspan="2">' + parameter.html + '</td>';
}
else if (parameter.title == undefined) {
//gap
html += '<td colspan="2"></td>';
}
else {
//locked fields without name
var str = "" + parameter.value;
var id_tmp = parameter.title.toLowerCase().replace(/[^\w]+/g, '').replace(/ +/g, '-');
id_tmp = id_tmp.substring(0, 10);
if (str.length < 40)
html += '<td colspan="2"><div class="trn" id="pop_data_' + id_tmp + '">' + parameter.value
+ '</div></td>';
else
html += '<td class="long_text_value" colspan="2"><textarea disabled="disabled">' + parameter.value
+ '</textarea></td>';
}
html += '</tr>';
}
html += '</table>';
return html;
}
//on key press inside input text
onkeyup(event) {
if (event.key == 'Enter') {
if (event.target.hasAttribute('data-prevent-submission')) {
event.preventDefault();
} else {
this.save();
}
}
}
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
strpos(haystack, needle, offset) {
var i = (haystack + '').indexOf(needle, (offset || 0));
return i === -1 ? false : i;
}
draw_background(canvas, W, H, gap, force) {
var transparent = this.Base_gui.get_transparency_support();
if (transparent == false && force == undefined) {
canvas.beginPath();
canvas.rect(0, 0, W, H);
canvas.fillStyle = "#ffffff";
canvas.fill();
return false;
}
if (gap == undefined)
gap = 10;
var fill = true;
for (var i = 0; i < W; i = i + gap) {
if (i % (gap * 2) == 0)
fill = true;
else
fill = false;
for (var j = 0; j < H; j = j + gap) {
if (fill == true) {
canvas.fillStyle = '#eeeeee';
canvas.fillRect(i, j, gap, gap);
fill = false;
}
else
fill = true;
}
}
}
}
export default Dialog_class;
@@ -0,0 +1,180 @@
/**
* ProgressOverlay — shared animated progress indicator for long AI operations.
*
* Usage:
* import { showProgress, updateProgress, hideProgress } from './progress_overlay.js';
*
* showProgress('Generating image…');
* updateProgress(50, 'Denoising step 15/30…'); // optional step updates
* hideProgress();
*
* When you don't have real step counts, call showProgress() and hideProgress() only —
* the bar animates automatically with a shimmer to signal activity.
*/
var _overlay = null;
var _bar = null;
var _label = null;
var _shimmerAnim = null;
var _fakeTimer = null;
var _currentPct = 0;
// ── SSE progress connection ───────────────────────────────────────────────────
var _sse = null;
/**
* Open an EventSource to /api/generate/progress and drive the bar with real
* denoising step counts from the local GPU pipeline.
*
* @param {string} pipeType - 'txt2img' | 'inpaint' | 'img2img'
* @param {string} baseUrl - window.API_BASE_URL or ''
*/
export function connectProgressSSE(pipeType, baseUrl) {
disconnectProgressSSE();
try {
var url = (baseUrl || '') + '/api/generate/progress';
_sse = new EventSource(url);
_sse.onmessage = (e) => {
try {
var states = JSON.parse(e.data);
var s = Array.isArray(states)
? states.find(st => st.pipeline === pipeType)
: null;
if (s && s.state === 'running' && s.total_steps) {
var pct = Math.round(s.step / s.total_steps * 85);
updateProgress(pct, s.message || `Step ${s.step} / ${s.total_steps}`);
}
} catch { /* malformed event — ignore */ }
};
_sse.onerror = () => disconnectProgressSSE();
} catch { /* SSE not supported */ }
}
export function disconnectProgressSSE() {
if (_sse) { _sse.close(); _sse = null; }
}
// ── Progress overlay ──────────────────────────────────────────────────────────
export function showProgress(message, estimatedSeconds) {
hideProgress();
_currentPct = 0;
// ── Backdrop ──────────────────────────────────────────────────────────────
_overlay = document.createElement('div');
_overlay.id = 'ai-progress-overlay';
_overlay.style.cssText = [
'position:fixed', 'inset:0', 'z-index:99999',
'display:flex', 'flex-direction:column',
'align-items:center', 'justify-content:center',
'background:rgba(0,0,0,0.55)',
'backdrop-filter:blur(2px)',
'-webkit-backdrop-filter:blur(2px)',
].join(';');
// ── Card ──────────────────────────────────────────────────────────────────
var card = document.createElement('div');
card.style.cssText = [
'background:#1a1a2e',
'border:1px solid #3a3a6a',
'border-radius:14px',
'padding:28px 36px',
'min-width:320px', 'max-width:480px',
'box-shadow:0 12px 48px rgba(0,0,0,0.8)',
'display:flex', 'flex-direction:column', 'gap:14px',
'text-align:center',
].join(';');
// ── Label ─────────────────────────────────────────────────────────────────
_label = document.createElement('div');
_label.textContent = message || 'Processing…';
_label.style.cssText = 'font-family:sans-serif;font-size:13px;color:#c0c0e0;line-height:1.4;min-height:2.8em';
// ── Track ─────────────────────────────────────────────────────────────────
var track = document.createElement('div');
track.style.cssText = [
'width:100%', 'height:6px',
'background:#0f0f2a',
'border-radius:3px',
'overflow:hidden',
'position:relative',
].join(';');
// ── Shimmer (indeterminate stripe) ────────────────────────────────────────
var shimmer = document.createElement('div');
shimmer.style.cssText = [
'position:absolute', 'inset:0',
'background:linear-gradient(90deg,transparent 0%,rgba(120,120,255,0.25) 50%,transparent 100%)',
'transform:translateX(-100%)',
'will-change:transform',
].join(';');
// ── Filled bar ────────────────────────────────────────────────────────────
_bar = document.createElement('div');
_bar.style.cssText = [
'position:absolute', 'inset-block:0', 'left:0',
'width:0%',
'background:linear-gradient(90deg,#5577ff,#88aaff)',
'border-radius:3px',
'transition:width 0.35s ease',
].join(';');
// ── Cancel hint ───────────────────────────────────────────────────────────
var hint = document.createElement('div');
hint.textContent = 'Press Esc to cancel';
hint.style.cssText = 'font-family:sans-serif;font-size:10px;color:#444;margin-top:2px';
track.appendChild(shimmer);
track.appendChild(_bar);
card.appendChild(_label);
card.appendChild(track);
card.appendChild(hint);
_overlay.appendChild(card);
document.body.appendChild(_overlay);
// Animate shimmer
var pos = -100;
_shimmerAnim = setInterval(() => {
pos += 2.5;
if (pos > 200) pos = -100;
shimmer.style.transform = `translateX(${pos}%)`;
}, 16);
// Fake progress that creeps toward 90% if no real steps given
if (estimatedSeconds) {
var totalMs = estimatedSeconds * 1000;
var step = 90 / (totalMs / 200);
_fakeTimer = setInterval(() => {
if (_currentPct < 90) {
_currentPct = Math.min(90, _currentPct + step);
_bar.style.width = _currentPct + '%';
}
}, 200);
}
// Esc to cancel
_overlay._escHandler = (e) => { if (e.key === 'Escape') hideProgress(); };
document.addEventListener('keydown', _overlay._escHandler);
}
export function updateProgress(pct, message) {
if (!_overlay) return;
_currentPct = Math.max(_currentPct, Math.min(100, pct));
if (_bar) _bar.style.width = _currentPct + '%';
if (_label && message) _label.textContent = message;
}
export function hideProgress() {
if (_shimmerAnim) { clearInterval(_shimmerAnim); _shimmerAnim = null; }
if (_fakeTimer) { clearInterval(_fakeTimer); _fakeTimer = null; }
if (_overlay) {
document.removeEventListener('keydown', _overlay._escHandler);
_overlay.remove();
_overlay = null;
}
_bar = null;
_label = null;
_currentPct = 0;
}
@@ -0,0 +1,195 @@
/**
* TIFF writer with support for:
* - Single-page RGBA (32-bit, interleaved, with alpha)
* - Single-page CMYK (8-bit per channel, no alpha — print-ready)
* - Multipage variants of both (one IFD per canvas layer)
*
* TIFF spec references: TIFF 6.0, ISO 12234-2 (CMYK)
* PhotometricInterpretation 2 = RGB, 5 = CMYK
*/
// --- RGB → CMYK conversion -------------------------------------------
function rgbToCmyk(r, g, b) {
var rn = r / 255, gn = g / 255, bn = b / 255;
var k = 1 - Math.max(rn, gn, bn);
if (k >= 1) return [0, 0, 0, 255];
var d = 1 - k;
return [
Math.round((d - rn) / d * 255),
Math.round((d - gn) / d * 255),
Math.round((d - bn) / d * 255),
Math.round(k * 255),
];
}
// --- Low-level TIFF binary builder ------------------------------------
/**
* Build a multipage TIFF buffer from an array of canvases.
*
* @param {HTMLCanvasElement[]} canvases
* @param {'rgba'|'cmyk'} colorMode
* @param {object} [opts]
* @param {boolean} [opts.littleEndian=false]
* @param {number} [opts.dpi=300]
* @returns {ArrayBuffer}
*/
function buildTIFF(canvases, colorMode, opts) {
opts = opts || {};
var lsb = !!opts.littleEndian;
var dpi = +(opts.dpi || 300) | 0;
var isCMYK = colorMode === 'cmyk';
// IFD field counts differ: RGBA has ExtraSamples tag, CMYK does not.
var ENTRY_COUNT = isCMYK ? 14 : 15;
var IFD_SIZE = 2 + ENTRY_COUNT * 12 + 4; // count + entries + nextIFD ptr
var FIELDS_SIZE = 64; // BPS(8)+XRes(8)+YRes(8)+sw(20)+dt(20)
var PAGE_OH = IFD_SIZE + FIELDS_SIZE;
// Compute page start offsets inside the final buffer.
var offsets = [];
var total = 8; // TIFF header
for (var i = 0; i < canvases.length; i++) {
offsets.push(total);
total += PAGE_OH + canvases[i].width * canvases[i].height * 4;
}
var buf = new ArrayBuffer(total);
var view = new DataView(buf);
var u8 = new Uint8Array(buf);
var pos = 0;
function s16(v) { view.setUint16(pos, v, lsb); pos += 2; }
function s32(v) { view.setUint32(pos, v, lsb); pos += 4; }
function entry(tag, type, count, value) {
s16(tag); s16(type); s32(count);
// SHORT with count==1 gets packed into the value field with padding.
if (type === 3 && count === 1) { s16(value); s16(0); }
else { s32(value); }
}
// Date helpers
var d = new Date();
var p2 = function(n) { return n < 10 ? '0' + n : '' + n; };
var dtStr = d.getFullYear() + ':' + p2(d.getMonth() + 1) + ':' + p2(d.getDate())
+ ' ' + p2(d.getHours()) + ':' + p2(d.getMinutes()) + ':' + p2(d.getSeconds());
var swStr = 'tiff-writer 1.0\0\0\0\0\0'; // 20 chars (null-padded)
// ---- TIFF header ----
s16(lsb ? 0x4949 : 0x4d4d);
s16(42);
s32(8); // offset to first IFD
// ---- Per-page IFDs + image data ----
for (var p = 0; p < canvases.length; p++) {
var cv = canvases[p];
var W = cv.width, H = cv.height;
var pageBase = offsets[p];
var fBase = pageBase + IFD_SIZE; // start of fields section
var imgBase = fBase + FIELDS_SIZE; // start of image data
var nextIFD = p + 1 < canvases.length ? offsets[p + 1] : 0;
// IFD entry count
s16(ENTRY_COUNT);
entry(0x00fe, 4, 1, 0); // NewSubfileType
entry(0x0100, 4, 1, W); // ImageWidth
entry(0x0101, 4, 1, H); // ImageLength
entry(0x0102, 3, 4, fBase); // BitsPerSample (offset → 4 shorts)
entry(0x0103, 3, 1, 1); // Compression: none
entry(0x0106, 3, 1, isCMYK ? 5 : 2); // PhotometricInterp: 5=CMYK, 2=RGB
entry(0x0111, 4, 1, imgBase); // StripOffsets
entry(0x0115, 3, 1, 4); // SamplesPerPixel: 4
entry(0x0117, 4, 1, W * H * 4); // StripByteCounts
entry(0x011a, 5, 1, fBase + 8); // XResolution
entry(0x011b, 5, 1, fBase + 16); // YResolution
entry(0x0128, 3, 1, 2); // ResolutionUnit: inch
entry(0x0131, 2, 20, fBase + 24); // Software (20 bytes)
entry(0x0132, 2, 20, fBase + 44); // DateTime (20 bytes)
if (!isCMYK) {
entry(0x0152, 3, 1, 2); // ExtraSamples: assoc. alpha (RGBA only)
}
s32(nextIFD);
// ---- Fields section (64 bytes) ----
// BitsPerSample: 8,8,8,8 as four SHORTs (8 bytes)
s16(8); s16(8); s16(8); s16(8);
// XResolution RATIONAL (8 bytes)
s32(dpi); s32(1);
// YResolution RATIONAL (8 bytes)
s32(dpi); s32(1);
// Software string (20 bytes, null-padded)
for (var i = 0; i < 20; i++)
view.setUint8(pos++, swStr.charCodeAt(i) & 0xff);
// DateTime string (20 bytes, null-padded)
for (var i = 0; i < 20; i++)
view.setUint8(pos++, i < dtStr.length ? dtStr.charCodeAt(i) & 0xff : 0);
// ---- Image data ----
var idata = cv.getContext('2d').getImageData(0, 0, W, H).data;
if (isCMYK) {
// Convert RGBA → CMYK and write 4 bytes per pixel (alpha discarded)
for (var px = 0, len = idata.length; px < len; px += 4) {
var cmyk = rgbToCmyk(idata[px], idata[px + 1], idata[px + 2]);
u8[pos++] = cmyk[0];
u8[pos++] = cmyk[1];
u8[pos++] = cmyk[2];
u8[pos++] = cmyk[3];
}
} else {
// Write RGBA directly
u8.set(idata, pos);
pos += idata.length;
}
}
return buf;
}
// --- Public API -------------------------------------------------------
var TiffWriter = {
/** Single-page 32-bit RGBA TIFF */
toRGBA: function(canvas, callback, opts) {
setTimeout(function() {
callback(buildTIFF([canvas], 'rgba', opts));
}, 9);
},
/** Single-page CMYK TIFF (print-ready, no alpha) */
toCMYK: function(canvas, callback, opts) {
setTimeout(function() {
callback(buildTIFF([canvas], 'cmyk', opts));
}, 9);
},
/** Multipage RGBA TIFF — one IFD per canvas in the array */
toMultipageRGBA: function(canvases, callback, opts) {
setTimeout(function() {
callback(buildTIFF(canvases, 'rgba', opts));
}, 9);
},
/** Multipage CMYK TIFF — one IFD per canvas in the array */
toMultipageCMYK: function(canvases, callback, opts) {
setTimeout(function() {
callback(buildTIFF(canvases, 'cmyk', opts));
}, 9);
},
/** Convenience: returns a Blob instead of ArrayBuffer */
toBlob: function(canvases, colorMode, callback, opts) {
if (!Array.isArray(canvases)) canvases = [canvases];
setTimeout(function() {
var buf = buildTIFF(canvases, colorMode, opts);
callback(new Blob([buf], { type: 'image/tiff' }));
}, 9);
},
};
export default TiffWriter;
export { rgbToCmyk };
+295
View File
@@ -0,0 +1,295 @@
import glfx from './glfx.js';
import ImageFilters from './imagefilters.js';
/**
* adds vintage effect
*
* @author ViliusL
*
* Functions:
* - adjust_color
* - lower_contrast
* - blur
* - light_leak
* - chemicals
* - exposure
* - grains
* - grains_big
* - optics
* - dusts
*
* Usage: VINTAGE.___function___(canvas,, param1, param2, ...);
*
* libs:
* - imagefilters.js, url: https://github.com/arahaya/ImageFilters.js
* - glfx.js url: http://evanw.github.com/glfx.js/
*/
class Vintage_class {
constructor(width, height) {
this.fx_filter = false;
this.exposure_rand = null;
this.lightLeakX = null;
this.lightLeakY = null;
this.reset_random_values(width, height);
}
/**
* apply all affect
*
* @param {canvas} canvas
* @param {int} level 0-100
*/
apply_all(canvas, level) {
//adjust from scale [0-100] to our scale.
var red_offset = level * 1; //[0, 100]
var contrast = level / 2; //[0, 50]
//var blur = level / 100; //[0, 1]
var light_leak = level * 1.5; //[0, 150]
var de_saturation = level * 1; //[0, 100]
var exposure = level * 1.5; //[0, 150]
var grains = level / 2; //[0, 50]
var big_grains = level / 5; //[0, 20]
var vignette_size = level / 200; //[0, 0.5]
var vignette_amount = level / 142; //[0, 0.7]
var dust_level = level * 1; //[0, 100]
this.adjust_color(canvas, red_offset);
this.lower_contrast(canvas, contrast);
//this.blur(canvas, blur);
this.light_leak(canvas, light_leak);
this.chemicals(canvas, de_saturation);
this.exposure(canvas, exposure);
this.grains(canvas, grains);
this.grains_big(canvas, big_grains);
this.optics(canvas, vignette_size, vignette_amount);
this.dusts(canvas, dust_level);
}
/**
* reset random values again.
*
* @param {int} width
* @param {int} height
*/
reset_random_values(width, height) {
this.exposure_rand = this.getRandomInt(1, 10);
this.lightLeakX = this.getRandomInt(0, width);
this.lightLeakY = this.getRandomInt(0, height);
}
//increasing red color
adjust_color(canvas, level_red) { //level = [0, 200], default 70
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
var param_green = 0;
var param_blue = 0;
var imageData = context.getImageData(0, 0, W, H);
var filtered = ImageFilters.ColorTransformFilter(imageData, 1, 1, 1, 1, level_red, param_green, param_blue, 1);
context.putImageData(filtered, 0, 0);
}
//decreasing contrast
lower_contrast(canvas, level) { //level = [0, 50], default 15
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
var imageData = context.getImageData(0, 0, W, H);
var filtered = ImageFilters.BrightnessContrastPhotoshop(imageData, 0, -level);
context.putImageData(filtered, 0, 0);
}
//adding blur
blur(canvas, level) { //level = [0, 2], default 0
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
if (level < 1)
return context;
var imageData = context.getImageData(0, 0, W, H);
var filtered = ImageFilters.GaussianBlur(imageData, level);
context.putImageData(filtered, 0, 0);
}
//creating transparent #ffa500 radial gradients
light_leak(canvas, level) { //level = [0, 150], default 90
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
var click_x = this.lightLeakX;
var click_y = this.lightLeakY;
var distance = Math.min(W, H) * 0.6;
var radgrad = context.createRadialGradient(
click_x, click_y, distance * level / 255,
click_x, click_y, distance);
radgrad.addColorStop(0, "rgba(255, 165, 0, " + level / 255 + ")");
radgrad.addColorStop(1, "rgba(255, 255, 255, 0)");
context.fillStyle = radgrad;
context.fillRect(0, 0, W, H);
}
//de-saturate
chemicals(canvas, level) { //level = [0, 100], default 40
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
var imageData = context.getImageData(0, 0, W, H);
var filtered = ImageFilters.HSLAdjustment(imageData, 0, -level, 0);
context.putImageData(filtered, 0, 0);
}
//creating transparent vertical black-to-white gradients
exposure(canvas, level) { //level = [0, 150], default 80
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
context.rect(0, 0, W, H);
var grd = context.createLinearGradient(0, 0, 0, H);
if (this.exposure_rand < 5) {
//dark at top
grd.addColorStop(0, "rgba(0, 0, 0, " + level / 255 + ")");
grd.addColorStop(1, "rgba(255, 255, 255, " + level / 255 + ")");
}
else {
//bright at top
grd.addColorStop(0, "rgba(255, 255, 255, " + level / 255 + ")");
grd.addColorStop(1, "rgba(0, 0, 0, " + level / 255 + ")");
}
context.fillStyle = grd;
context.fill();
}
//add grains, noise
grains(canvas, level) { //level = [0, 50], default 10
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
if (level == 0)
return context;
var img = context.getImageData(0, 0, W, H);
var imgData = img.data;
for (var j = 0; j < H; j++) {
for (var i = 0; i < W; i++) {
var x = (i + j * W) * 4;
if (imgData[x + 3] == 0)
continue; //transparent
//increase it's lightness
var delta = this.getRandomInt(0, level);
if (delta == 0)
continue;
if (imgData[x] - delta < 0)
imgData[x] = -(imgData[x] - delta);
else
imgData[x] = imgData[x] - delta;
if (imgData[x + 1] - delta < 0)
imgData[x + 1] = -(imgData[x + 1] - delta);
else
imgData[x + 1] = imgData[x + 1] - delta;
if (imgData[x + 2] - delta < 0)
imgData[x + 2] = -(imgData[x + 2] - delta);
else
imgData[x + 2] = imgData[x + 2] - delta;
}
}
context.putImageData(img, 0, 0);
}
//add big grains, noise
grains_big(canvas, level) { //level = [0, 50], default 20
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
if (level == 0)
return context;
var n = W * H / 100 * level; //density
var color = 200;
for (var i = 0; i < n; i++) {
var power = this.getRandomInt(5, 10 + level);
var size = 2;
var x = this.getRandomInt(0, W);
var y = this.getRandomInt(0, H);
context.fillStyle = "rgba(" + color + ", " + color + ", " + color + ", " + power / 255 + ")";
context.fillRect(x, y, size, size);
}
}
//adding vignette effect - blurred dark borders
optics(canvas, param1, param2) { //param1 [0, 0.5], param2 [0, 0.7], default 0.3, 0.5
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
if (this.fx_filter == false) {
//init glfx lib
this.fx_filter = glfx.canvas();
}
var texture = this.fx_filter.texture(context.getImageData(0, 0, W, H));
this.fx_filter.draw(texture).vignette(param1, param2).update();
context.drawImage(this.fx_filter, 0, 0);
}
//add dust and hairs
dusts(canvas, level) { //level = [0, 100], default 70
var context = canvas.getContext("2d");
var W = canvas.width;
var H = canvas.height;
var n = level / 100 * (W * H) / 1000;
//add dust
context.fillStyle = "rgba(200, 200, 200, 0.3)";
for (var i = 0; i < n; i++) {
var x = this.getRandomInt(0, W);
var y = this.getRandomInt(0, H);
var mode = this.getRandomInt(1, 2);
if (mode == 1) {
var w = 1;
var h = this.getRandomInt(1, 3);
}
else if (mode == 2) {
var w = this.getRandomInt(1, 3);
var h = 1;
}
context.beginPath();
context.rect(x, y, w, h);
context.fill();
}
//add hairs
context.strokeStyle = "rgba(200, 200, 200, 0.2)";
for (var i = 0; i < n / 20; i++) {
var x = this.getRandomInt(0, W);
var y = this.getRandomInt(0, H);
var radius = this.getRandomInt(5, 10);
var start_nr = this.getRandomInt(0, 20) / 10;
var start_angle = Math.PI * start_nr;
var end_angle = Math.PI * (start_nr + this.getRandomInt(7, 15) / 10);
context.beginPath();
context.arc(x, y, radius, start_angle, end_angle);
context.stroke();
}
return context;
}
//random number generator
getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
}
export default Vintage_class;
+150
View File
@@ -0,0 +1,150 @@
//handles zoom and pan
//https://stackoverflow.com/questions/44009094/how-to-bound-image-pan-when-zooming-html-canvas/44015705#44015705
const zoomView = (() => {
const matrix = [1, 0, 0, 1, 0, 0]; // current view transform
const invMatrix = [1, 0, 0, 1, 0, 0]; // current inverse view transform
var m = matrix; // alias
var im = invMatrix; // alias
var scale = 1; // current scale
const bounds = {
top: 0,
left: 0,
right: 200,
bottom: 200,
};
var useConstraint = true; // if true then limit pan and zoom to
// keep bounds within the current context
var maxScale = 1;
const workPoint1 = {x: 0, y: 0};
const workPoint2 = {x: 0, y: 0};
const wp1 = workPoint1; // alias
const wp2 = workPoint2; // alias
var ctx;
const pos = {// current position of origin
x: 0,
y: 0,
};
var dirty = true;
const API = {
canvasDefault() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
},
apply() {
if (dirty) {
this.update();
}
ctx.setTransform(m[0], m[1], m[2], m[3], m[4], m[5]);
},
getPosition() {
return { x: pos.x, y: pos.y };
},
getContext() {
return ctx;
},
getBounds() {
return bounds;
},
getScale() {
return scale;
},
getMaxScale() {
return maxScale;
},
matrix, // expose the matrix
invMatrix, // expose the inverse matrix
update() { // call to update transforms
dirty = false;
m[3] = m[0] = scale;
m[1] = m[2] = 0;
m[4] = pos.x;
m[5] = pos.y;
if (useConstraint) {
this.constrain();
}
this.invScale = 1 / scale;
// calculate the inverse transformation
var cross = m[0] * m[3] - m[1] * m[2];
im[0] = m[3] / cross;
im[1] = -m[1] / cross;
im[2] = -m[2] / cross;
im[3] = m[0] / cross;
},
constrain() {
maxScale = Math.min(
ctx.canvas.width / (bounds.right - bounds.left),
ctx.canvas.height / (bounds.bottom - bounds.top)
);
if (scale < maxScale) {
m[0] = m[3] = scale = maxScale;
}
wp1.x = bounds.left;
wp1.y = bounds.top;
this.toScreen(wp1, wp2);
if (wp2.x > 0) {
m[4] = pos.x -= wp2.x;
}
if (wp2.y > 0) {
m[5] = pos.y -= wp2.y;
}
wp1.x = bounds.right;
wp1.y = bounds.bottom;
this.toScreen(wp1, wp2);
if (wp2.x < ctx.canvas.width) {
m[4] = (pos.x -= wp2.x - ctx.canvas.width);
}
if (wp2.y < ctx.canvas.height) {
m[5] = (pos.y -= wp2.y - ctx.canvas.height);
}
},
toWorld(from_x, from_y) { // convert screen to world coords
var xx, yy;
var pointW = {};
if (dirty) {
this.update();
}
xx = from_x - m[4];
yy = from_y - m[5];
pointW.x = xx * im[0] + yy * im[2];
pointW.y = xx * im[1] + yy * im[3];
return pointW;
},
toScreen(from, point = {}){ // convert world coords to screen coords
if (dirty) {
this.update();
}
point.x = from.x * m[0] + from.y * m[2] + m[4];
point.y = from.x * m[1] + from.y * m[3] + m[5];
return point;
},
scaleAt(x_from, y_from, amount) { // at in screen coords
if (dirty) {
this.update();
}
scale *= amount;
pos.x = x_from - (x_from - pos.x) * amount;
pos.y = y_from - (y_from - pos.y) * amount;
dirty = true;
},
move(move_x, move_y) { // move is in screen coords
pos.x += move_x;
pos.y += move_y;
dirty = true;
},
setContext(context) {
ctx = context;
dirty = true;
},
setBounds(top, left, right, bottom) {
bounds.top = top;
bounds.left = left;
bounds.right = right;
bounds.bottom = bottom;
useConstraint = true;
dirty = true;
}
};
return API;
})();
export default zoomView;
+127
View File
@@ -0,0 +1,127 @@
/**
* miniPaint - https://github.com/viliusle/miniPaint
* author: Vilius L.
*/
//css
import './../css/reset.css';
import './../css/utility.css';
import './../css/component.css';
import './../css/layout.css';
import './../css/menu.css';
import './../css/print.css';
import './../../node_modules/alertifyjs/build/css/alertify.min.css';
//js
import app from './app.js';
import config from './config.js';
import './core/components/index.js';
import Base_gui_class from './core/base-gui.js';
import Base_layers_class from './core/base-layers.js';
import Base_tools_class from './core/base-tools.js';
import Base_state_class from './core/base-state.js';
import Base_search_class from './core/base-search.js';
import File_open_class from './modules/file/open.js';
import File_save_class from './modules/file/save.js';
import * as Actions from './actions/index.js';
import { mountProviderBadge } from './core/components/provider-badge.js';
window.addEventListener('load', function (e) {
// Initiate app
var Layers = new Base_layers_class();
var Base_tools = new Base_tools_class(true);
var GUI = new Base_gui_class();
var Base_state = new Base_state_class();
var File_open = new File_open_class();
var File_save = new File_save_class();
var Base_search = new Base_search_class();
// Register singletons in app module
app.Actions = Actions;
app.Config = config;
app.FileOpen = File_open;
app.FileSave = File_save;
app.GUI = GUI;
app.Layers = Layers;
app.State = Base_state;
app.Tools = Base_tools;
// Register as global for quick or external access
window.Layers = Layers;
window.AppConfig = config;
window.State = Base_state;
window.FileOpen = File_open;
window.FileSave = File_save;
// Render all
GUI.init();
Layers.init();
// Mount provider badge in the tools panel footer
mountProviderBadge(document.getElementById('tools_container') || document.body);
// Collapse right-panel Colors section by default (compact color swatch on left toolbar instead)
_collapseColorsPanel();
// Mount compact foreground/background color swatches at bottom of left toolbar
_mountToolbarColorSwatch();
}, false);
function _collapseColorsPanel() {
var toggle = document.querySelector('[data-target="toggle_colors"]');
var panel = document.getElementById('toggle_colors');
if (toggle && panel) {
// Only collapse if user hasn't explicitly expanded it (no saved cookie)
var Helper = { getCookie: (k) => { var m = document.cookie.match('(^|;)\\s*' + k + '\\s*=\\s*([^;]+)'); return m ? m.pop() : null; } };
if (Helper.getCookie('toggle_colors') !== '1') {
panel.classList.add('hidden');
toggle.classList.add('toggled');
}
}
}
function _mountToolbarColorSwatch() {
var toolbar = document.getElementById('tools_container');
if (!toolbar) return;
// Spacer to push swatch to bottom
var spacer = document.createElement('div');
spacer.style.cssText = 'flex:1;min-height:8px;width:100%;';
toolbar.appendChild(spacer);
// Foreground / background color squares (click to open full color picker)
var wrap = document.createElement('div');
wrap.id = 'toolbar_color_swatch';
wrap.title = 'Foreground / Background color — click to open color picker';
wrap.style.cssText = 'position:relative;width:30px;height:30px;margin:4px 0 4px 5px;cursor:pointer;flex-shrink:0;';
wrap.innerHTML = `
<div id="tc_bg" style="position:absolute;right:0;bottom:0;width:20px;height:20px;
border:1px solid #555;background:#000;border-radius:3px;"></div>
<div id="tc_fg" style="position:absolute;left:0;top:0;width:20px;height:20px;
border:1px solid #777;background:#008000;border-radius:3px;"></div>`;
toolbar.appendChild(wrap);
// Keep swatch in sync with config.COLOR
function _syncSwatch() {
var fg = document.getElementById('tc_fg');
var bg = document.getElementById('tc_bg');
if (fg) fg.style.background = window.config && config.COLOR ? config.COLOR : '#008000';
}
setInterval(_syncSwatch, 250);
// Click → open the right-side color panel
wrap.addEventListener('click', function () {
var panel = document.getElementById('toggle_colors');
var toggle = document.querySelector('[data-target="toggle_colors"]');
if (!panel) return;
var hidden = panel.classList.contains('hidden');
if (hidden) {
panel.classList.remove('hidden');
if (toggle) toggle.classList.remove('toggled');
// Scroll right panel to top so color picker is visible
var sidebar = document.querySelector('.sidebar_right');
if (sidebar) sidebar.scrollTop = 0;
} else {
panel.classList.add('hidden');
if (toggle) toggle.classList.add('toggled');
}
});
}
@@ -0,0 +1,82 @@
import config from "../../config";
import Base_layers_class from './../../core/base-layers.js';
import File_save_class from './../file/save.js';
import Helper_class from './../../libs/helpers.js';
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
var instance = null;
class Copy_class {
constructor() {
//singleton
if (instance) {
return instance;
}
instance = this;
this.Base_layers = new Base_layers_class();
this.Helper = new Helper_class();
this.File_save = new File_save_class();
//events
document.addEventListener('keydown', (event) => {
var code = event.key.toLowerCase();
var ctrlDown = event.ctrlKey || event.metaKey;
if (this.Helper.is_input(event.target))
return;
if (code == "c" && ctrlDown == true) {
//copy to clipboard
this.copy_to_clipboard();
}
}, false);
}
async copy_to_clipboard(){
var _this = this;
const canWriteToClipboard = await this.askWritePermission();
if (canWriteToClipboard) {
//get data - current layer
var canvas = this.Base_layers.convert_layer_to_canvas();
var ctx = canvas.getContext("2d");
if (config.TRANSPARENCY == false) {
//add white background
ctx.globalCompositeOperation = 'destination-over';
this.File_save.fillCanvasBackground(ctx, '#ffffff');
ctx.globalCompositeOperation = 'source-over';
}
//save using lib
canvas.toBlob(function (blob) {
_this.setToClipboard(blob);
});
}
else{
alertify.error('Missing permissions to write to Clipboard.cc');
}
}
async setToClipboard(blob) {
const data = [new ClipboardItem({ [blob.type]: blob })];
await navigator.clipboard.write(data);
}
async askWritePermission() {
try {
// The clipboard-write permission is granted automatically to pages
// when they are the active tab. So it's not required, but it's more safe.
const { state } = await navigator.permissions.query({ name: 'clipboard-write' })
return state === 'granted';
}
catch (error) {
// Browser compatibility / Security error (ONLY HTTPS) ...
return false;
}
}
}
export default Copy_class;

Some files were not shown because too many files have changed in this diff Show More