hangwin--mcp-chrome
c7f1624ebc
- Translated all Chinese comments in app/chrome-extension/utils/ to English - Removed redundant comments that were obvious from the code - Preserved valuable technical explanations and complex logic comments - Improved code internationalization and readability Files modified: - content-indexer.ts: Translated database cleanup and initialization comments - semantic-similarity-engine.ts: Translated model comparison, memory pool, and SIMD comments - simd-math-engine.ts: Translated SIMD optimization and browser detection comments - vector-database.ts: Translated vector operations, database management, and cleanup comments - image-utils.ts: Removed redundant whitespace and obvious comments
153 行
3.9 KiB
TypeScript
153 行
3.9 KiB
TypeScript
/**
|
|
* Image processing utility functions
|
|
*/
|
|
|
|
/**
|
|
* Create ImageBitmap from data URL (for OffscreenCanvas)
|
|
* @param dataUrl Image data URL
|
|
* @returns Created ImageBitmap object
|
|
*/
|
|
export async function createImageBitmapFromUrl(dataUrl: string): Promise<ImageBitmap> {
|
|
const response = await fetch(dataUrl);
|
|
const blob = await response.blob();
|
|
return await createImageBitmap(blob);
|
|
}
|
|
|
|
/**
|
|
* Stitch multiple image parts (dataURL) onto a single canvas
|
|
* @param parts Array of image parts, each containing dataUrl and y coordinate
|
|
* @param totalWidthPx Total width (pixels)
|
|
* @param totalHeightPx Total height (pixels)
|
|
* @returns Stitched canvas
|
|
*/
|
|
export async function stitchImages(
|
|
parts: { dataUrl: string; y: number }[],
|
|
totalWidthPx: number,
|
|
totalHeightPx: number,
|
|
): Promise<OffscreenCanvas> {
|
|
const canvas = new OffscreenCanvas(totalWidthPx, totalHeightPx);
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
if (!ctx) {
|
|
throw new Error('Unable to get canvas context');
|
|
}
|
|
|
|
ctx.fillStyle = '#FFFFFF';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
|
for (const part of parts) {
|
|
try {
|
|
const img = await createImageBitmapFromUrl(part.dataUrl);
|
|
const sx = 0;
|
|
const sy = 0;
|
|
const sWidth = img.width;
|
|
let sHeight = img.height;
|
|
const dy = part.y;
|
|
|
|
if (dy + sHeight > totalHeightPx) {
|
|
sHeight = totalHeightPx - dy;
|
|
}
|
|
|
|
if (sHeight <= 0) continue;
|
|
|
|
ctx.drawImage(img, sx, sy, sWidth, sHeight, 0, dy, sWidth, sHeight);
|
|
} catch (error) {
|
|
console.error('Error stitching image part:', error, part);
|
|
}
|
|
}
|
|
return canvas;
|
|
}
|
|
|
|
/**
|
|
* Crop image (from dataURL) to specified rectangle and resize
|
|
* @param originalDataUrl Original image data URL
|
|
* @param cropRectPx Crop rectangle (physical pixels)
|
|
* @param dpr Device pixel ratio
|
|
* @param targetWidthOpt Optional target output width (CSS pixels)
|
|
* @param targetHeightOpt Optional target output height (CSS pixels)
|
|
* @returns Cropped canvas
|
|
*/
|
|
export async function cropAndResizeImage(
|
|
originalDataUrl: string,
|
|
cropRectPx: { x: number; y: number; width: number; height: number },
|
|
dpr: number = 1,
|
|
targetWidthOpt?: number,
|
|
targetHeightOpt?: number,
|
|
): Promise<OffscreenCanvas> {
|
|
const img = await createImageBitmapFromUrl(originalDataUrl);
|
|
|
|
let sx = cropRectPx.x;
|
|
let sy = cropRectPx.y;
|
|
let sWidth = cropRectPx.width;
|
|
let sHeight = cropRectPx.height;
|
|
|
|
// Ensure crop area is within image boundaries
|
|
if (sx < 0) {
|
|
sWidth += sx;
|
|
sx = 0;
|
|
}
|
|
if (sy < 0) {
|
|
sHeight += sy;
|
|
sy = 0;
|
|
}
|
|
if (sx + sWidth > img.width) {
|
|
sWidth = img.width - sx;
|
|
}
|
|
if (sy + sHeight > img.height) {
|
|
sHeight = img.height - sy;
|
|
}
|
|
|
|
if (sWidth <= 0 || sHeight <= 0) {
|
|
throw new Error('Invalid calculated crop size (<=0). Element may not be visible or fully captured.');
|
|
}
|
|
|
|
const finalCanvasWidthPx = targetWidthOpt ? targetWidthOpt * dpr : sWidth;
|
|
const finalCanvasHeightPx = targetHeightOpt ? targetHeightOpt * dpr : sHeight;
|
|
|
|
const canvas = new OffscreenCanvas(finalCanvasWidthPx, finalCanvasHeightPx);
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
if (!ctx) {
|
|
throw new Error('Unable to get canvas context');
|
|
}
|
|
|
|
ctx.drawImage(
|
|
img,
|
|
sx,
|
|
sy,
|
|
sWidth,
|
|
sHeight,
|
|
0,
|
|
0,
|
|
finalCanvasWidthPx,
|
|
finalCanvasHeightPx,
|
|
);
|
|
|
|
return canvas;
|
|
}
|
|
|
|
/**
|
|
* Convert canvas to data URL
|
|
* @param canvas Canvas
|
|
* @param format Image format
|
|
* @param quality JPEG quality (0-1)
|
|
* @returns Data URL
|
|
*/
|
|
export async function canvasToDataURL(
|
|
canvas: OffscreenCanvas,
|
|
format: string = 'image/png',
|
|
quality?: number,
|
|
): Promise<string> {
|
|
const blob = await canvas.convertToBlob({
|
|
type: format,
|
|
quality: format === 'image/jpeg' ? quality : undefined,
|
|
});
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.onloadend = () => resolve(reader.result as string);
|
|
reader.onerror = reject;
|
|
reader.readAsDataURL(blob);
|
|
});
|
|
}
|