diff --git a/doric-demo/src/ImageProcesserDemo.tsx b/doric-demo/src/ImageProcesserDemo.tsx deleted file mode 100644 index 83809a08..00000000 --- a/doric-demo/src/ImageProcesserDemo.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { - Base64Resource, - DrawableResource, - Group, - Panel, - jsx, - Color, - layoutConfig, - Image, - RemoteResource, - MainBundleResource, - Scroller, - VLayout, - Text, - Gravity, - createRef, - loge, -} from "doric"; -import { colors, label } from "./utils"; -import { img_base64 } from "./image_base64"; - -@Entry -export class ImageProcessorDemo extends Panel { - build(root: Group): void { - const iv = createRef(); - - - - Image Processor - - - { - const imageInfo = await iv.current.getImageInfo(context); - loge(imageInfo); - const pixels = await iv.current.getImagePixels(context); - loge(pixels.byteLength); - const data = new Uint8Array(pixels); - for (let i = 0; i < data.length - 4; i += 4) { - data[i + 3] = 12; - } - iv.current.imagePixels = { - width: imageInfo.width, - height: imageInfo.height, - pixels: pixels, - }; - }} - > - Transparent - - - ; - } -} diff --git a/doric-demo/src/ImageProcessorDemo.tsx b/doric-demo/src/ImageProcessorDemo.tsx new file mode 100644 index 00000000..8da2e4df --- /dev/null +++ b/doric-demo/src/ImageProcessorDemo.tsx @@ -0,0 +1,411 @@ +import { + Group, + Panel, + jsx, + Color, + layoutConfig, + Image, + Scroller, + VLayout, + Text, + Gravity, + createRef, + loge, +} from "doric"; +import { colors } from "./utils"; + +function pixelToRGBA(pixel: number) { + const r = pixel & 0xff; + const g = (pixel >> 8) & 0xff; + const b = (pixel >> 16) & 0xff; + const a = (pixel >> 24) & 0xff; + return { r, g, b, a }; +} + +function rgbaToPixel(rgba: { r: number; g: number; b: number; a: number }) { + return ( + (rgba.r & 0xff) + + ((rgba.g & 0xff) << 8) + + ((rgba.b & 0xff) << 16) + + ((rgba.a & 0xff) << 24) + ); +} + +@Entry +export class ImageProcessorDemo extends Panel { + build(root: Group): void { + const iv = createRef(); + const imageUrl = "https://doric.pub/about/The_Parthenon_in_Athens.jpg"; //"https://doric.pub/logo.png"; + + + + 图片处理 + + + {( + [ + [ + "黑白", + async () => { + const imageInfo = await iv.current.getImageInfo(context); + const pixels = await iv.current.getImagePixels(context); + const data = new Uint32Array(pixels); + for (let i = 0; i < data.length; i++) { + let { r, g, b, a } = pixelToRGBA(data[i]); + r = g = b = Math.floor(r * 0.2989 + g * 0.587 + b * 0.114); + data[i] = rgbaToPixel({ r, g, b, a }); + } + iv.current.imagePixels = { + width: imageInfo.width, + height: imageInfo.height, + pixels: pixels, + }; + }, + ], + [ + "FastBlur", + async () => { + const imageInfo = await iv.current.getImageInfo(context); + const pixels = await iv.current.getImagePixels(context); + const data = new Uint32Array(pixels); + const radius = 30; + const w = imageInfo.width; + const h = imageInfo.height; + const wm = w - 1; + const hm = h - 1; + const wh = w * h; + const div = 2 * radius + 1; + const r: number[] = new Array(wh); + const g: number[] = new Array(wh); + const b: number[] = new Array(wh); + let rsum = 0, + gsum = 0, + bsum = 0, + x, + y, + i, + p, + yp, + yi, + yw; + const vmin: number[] = new Array(Math.max(w, h)); + let divsum = (div + 1) >> 1; + divsum *= divsum; + const dv: number[] = new Array(256 * divsum); + for (i = 0; i < 256 * divsum; i++) { + dv[i] = Math.round(i / divsum); + } + + yw = yi = 0; + + const stack = new Array(div).fill(0).map((_) => new Array(3)); + let stackpointer; + let stackstart; + let sir: number[]; + let rbs; + let r1 = radius + 1; + let routsum, goutsum, boutsum; + let rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = + ginsum = + binsum = + routsum = + goutsum = + boutsum = + rsum = + gsum = + bsum = + 0; + for (i = -radius; i <= radius; i++) { + p = data[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = p & 0xff; + sir[1] = (p >> 8) & 0xff; + sir[2] = (p >> 16) & 0xff; + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = data[yw + vmin[x]]; + + sir[0] = p & 0xff; + sir[1] = (p >> 8) & 0xff; + sir[2] = (p >> 16) & 0xff; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = + ginsum = + binsum = + routsum = + goutsum = + boutsum = + rsum = + gsum = + bsum = + 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + data[yi] = + dv[rsum] | + ((dv[gsum] & 0xff) << 8) | + ((dv[bsum] & 0xff) << 16) | + (data[yi] & 0xff000000); + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + iv.current.imagePixels = { + width: imageInfo.width, + height: imageInfo.height, + pixels: pixels, + }; + loge(stack); + }, + ], + [ + "高斯模糊", + async () => { + const imageInfo = await iv.current.getImageInfo(context); + loge(imageInfo); + const pixels = await iv.current.getImagePixels(context); + const data = new Uint32Array(pixels); + function getPixel(x: number, y: number) { + return data[ + Math.max(0, Math.min(x, imageInfo.width - 1)) + + Math.max(0, Math.min(y, imageInfo.height - 1)) * + imageInfo.width + ]; + } + function gaussian(x: number, y: number) { + const q = 1.5; + return ( + (1 / (2 * Math.PI * q * q)) * + Math.exp(-(x * x + y * y) / (2 * q * q)) + ); + } + const radius = 1; + const weights: number[][] = new Array(radius * 2 + 1) + .fill(0) + .map((_) => new Array(radius * 2 + 1).fill(0)); + let allWeight = 0; + for (let x = 0; x < radius * 2 + 1; x++) { + for (let y = 0; y < radius * 2 + 1; y++) { + weights[x][y] = gaussian(x - radius, y - radius); + allWeight += weights[x][y]; + } + } + + for (let x = 0; x < radius * 2 + 1; x++) { + for (let y = 0; y < radius * 2 + 1; y++) { + weights[x][y] = weights[x][y] / allWeight; + } + } + + for (let j = 0; j < imageInfo.height; j++) { + for (let i = 0; i < imageInfo.width; i++) { + const rgba = { r: 0, g: 0, b: 0, a: 0 }; + for (let lx = 0; lx < radius * 2 + 1; lx++) { + for (let ly = 0; ly < radius * 2 + 1; ly++) { + const { r, g, b, a } = pixelToRGBA( + getPixel(lx - radius + i, ly - radius + j) + ); + rgba.r += r * weights[lx][ly]; + rgba.g += g * weights[lx][ly]; + rgba.b += b * weights[lx][ly]; + rgba.a += a * weights[lx][ly]; + } + } + data[i + j * imageInfo.width] = rgbaToPixel(rgba); + } + } + iv.current.imagePixels = { + width: imageInfo.width, + height: imageInfo.height, + pixels: pixels, + }; + }, + ], + [ + "透明", + async () => { + const imageInfo = await iv.current.getImageInfo(context); + loge(imageInfo); + const pixels = await iv.current.getImagePixels(context); + loge(pixels.byteLength); + const data = new Uint8Array(pixels); + for (let i = 0; i < data.length - 4; i += 4) { + data[i + 3] = 0xff / 2; + } + iv.current.imagePixels = { + width: imageInfo.width, + height: imageInfo.height, + pixels: pixels, + }; + }, + ], + [ + "重置", + async () => { + iv.current.imageUrl = undefined; + iv.current.imageUrl = imageUrl; + }, + ], + ] as [string, () => {}][] + ).map((e) => ( + + {e[0]} + + ))} + + ; + } +} diff --git a/doric-demo/src/ResourceDemo.tsx b/doric-demo/src/ResourceDemo.tsx index 87af564c..909fe8ad 100644 --- a/doric-demo/src/ResourceDemo.tsx +++ b/doric-demo/src/ResourceDemo.tsx @@ -30,7 +30,7 @@ export class ResourceDemo extends Panel { loge(pixels.byteLength); const data = new Uint8Array(pixels); for (let i = 0; i < data.length - 4; i += 4) { - data[i + 3] = 0xff / 3; + data[i + 3] = 0xff / 5; } iv.current.imagePixels = { width: imageInfo.width, @@ -63,12 +63,12 @@ export class ResourceDemo extends Panel { } /> { await click();