• wasm示例 js canvas 动画示例


    利用wasm绘制图的一些参考:

    fhtr.org/gravityring/sprites.html

    用Canvas + WASM画一个迷宫 - 知乎 (zhihu.com)

    WebGL 重置画布尺寸 (webglfundamentals.org)


     canvaskit demo 

    https://demos.skia.org/demo/

    src\third_party\skia\demos.skia.org\Makefile

    根据它可以本地启动示例:python -m SimpleHTTPServer 8123

    访问:http://localhost:8123/demos/hello_world/index.html

    如果需要下载的js访问不到,替换成本地的:   <script type="text/javascript" src="https://unpkg.com/canvaskit-wasm@latest/bin/full/canvaskit.js"></script>

     

    skp 在线的背后源码:

    E:\dev\chromium96\src\third_party\skia\modules\canvaskit\wasm_tools

    目录:
    E:\dev\chromium96\src\third_party\skia\experimental\wasm-skp-debugger
    E:\dev\chromium96\src\third_party\skia\tools\debugger

    关联:
    E:\dev\chromium96\src\third_party\skia\experimental\wasm-skp-debugger\debugger_bindings.cpp

    #include "tools/debugger/DebugCanvas.h"

    #include "tools/debugger/DebugLayerManager.h"

    debuggerz网站源码???:

    third_party/skia/modules/canvaskit/debugger_bindings.cpp

    这里封装了 供js去调用

    class SkpDebugPlayer {
      public:
        SkpDebugPlayer() :
        udm(UrlDataManager(SkString("/data"))){}
    。。。。。。
    
    }
    View Code

    类里面的方法

        /* loadSkp deserializes a skp file that has been copied into the shared WASM memory.
         * cptr - a pointer to the data to deserialize.
         * length - length of the data in bytes.
         * The caller must allocate the memory with M._malloc where M is the wasm module in javascript
         * and copy the data into M.buffer at the pointer returned by malloc.
         *
         * uintptr_t is used here because emscripten will not allow binding of functions with pointers
         * to primitive types. We can instead pass a number and cast it to whatever kind of
         * pointer we're expecting.
         *
         * Returns an error string which is populated in the case that the file cannot be read.
         */
        std::string loadSkp(uintptr_t cptr, int length) {
          const uint8_t* data = reinterpret_cast<const uint8_t*>(cptr);
          // Both traditional and multi-frame skp files have a magic word
          SkMemoryStream stream(data, length);
          SkDebugf("make stream at %p, with %d bytes\n",data, length);
          const bool isMulti = memcmp(data, kMultiMagic, sizeof(kMultiMagic) - 1) == 0;
    
    
          if (isMulti) {
            SkDebugf("Try reading as a multi-frame skp\n");
            const auto& error = loadMultiFrame(&stream);
            if (!error.empty()) { return error; }
          } else {
            SkDebugf("Try reading as single-frame skp\n");
            // TODO(nifong): Rely on SkPicture's return errors once it provides some.
            frames.push_back(loadSingleFrame(&stream));
          }
          return "";
        }
    View Code

     js的调用:src\third_party\skia\experimental\wasm-skp-debugger\tests\startup.spec.js

        it('can load and draw a skp file on a Web GL canvas', function(done) {
            LoadDebugger.then(catchException(done, () => {
                const surface = Debugger.MakeWebGLCanvasSurface(
                    document.getElementById('debugger_view'));
    
                fetch('/debugger/sample.skp').then(function(response) {
                    // Load test file
                    if (!response.ok) {
                      throw new Error("HTTP error, status = " + response.status);
                    }
                    response.arrayBuffer().then(function(buffer) {
                        const fileContents = new Uint8Array(buffer);
                        console.log('fetched /debugger/sample.skp');
                        const player = Debugger.SkpFilePlayer(fileContents);
                        // Draw picture
                        player.drawTo(surface, 789); // number of commands in sample file
                        surface.flush();
    
                        console.log('drew picture to canvas element');
                        surface.dispose();
                        done();
                    });
                  });
            }));
        });
    View Code

    third_party/skia/tools/debugger/DebugCanvas.h

    画多个skp,背景透明问题

    debugger_bindings.cpp中的代码:

        /* drawTo asks the debug canvas to draw from the beginning of the picture
         * to the given command and flush the canvas.
         */
        void drawTo(SkSurface* surface, int32_t index) {
          // Set the command within the frame or layer event being drawn.
          if (fInspectedLayer >= 0) {
            fLayerManager->setCommand(fInspectedLayer, fp, index);
          } else {
            index = constrainFrameCommand(index);
          }
    
          auto* canvas = surface->getCanvas();
          canvas->clear(SK_ColorTRANSPARENT);
          if (fInspectedLayer >= 0) {
            // when it's a layer event we're viewing, we use the layer manager to render it.
            fLayerManager->drawLayerEventTo(surface, fInspectedLayer, fp);
          } else {
            // otherwise, its a frame at the top level.
            frames[fp]->drawTo(surface->getCanvas(), index);
          }
          surface->flush();
        }
    
        // Draws to the end of the current frame.
        void draw(SkSurface* surface) {
          auto* canvas = surface->getCanvas();
          canvas->clear(SK_ColorTRANSPARENT);
          frames[fp]->draw(surface->getCanvas());
          surface->getCanvas()->flush();
        }
        /**
            Executes all draw calls to the canvas.
            @param canvas  The canvas being drawn to
         */
        void draw(SkCanvas* canvas);
    
        /**
            Executes the draw calls up to the specified index.
            Does not clear the canvas to transparent black first,
            if needed, caller should do that first.
            @param canvas  The canvas being drawn to
            @param index  The index of the final command being executed
            @param m an optional Mth gpu op to highlight, or -1
         */
        void drawTo(SkCanvas* canvas, int index, int m = -1);

    对上面头文件的实现:third_party/skia/tools/debugger/DebugCanvas.cpp

    void DebugCanvas::drawTo(SkCanvas* originalCanvas, int index, int m) {
        SkASSERT(!fCommandVector.isEmpty());
        SkASSERT(index < fCommandVector.count());
    
        int saveCount = originalCanvas->save();
    
        originalCanvas->resetMatrix();
        SkCanvasPriv::ResetClip(originalCanvas);
    
        DebugPaintFilterCanvas filterCanvas(originalCanvas);
        SkCanvas* finalCanvas = fOverdrawViz ? &filterCanvas : originalCanvas;
    
    #if SK_GPU_V1
        auto dContext = GrAsDirectContext(finalCanvas->recordingContext());
    
        // If we have a GPU backend we can also visualize the op information
        GrAuditTrail* at = nullptr;
        if (fDrawGpuOpBounds || m != -1) {
            // The audit trail must be obtained from the original canvas.
            at = this->getAuditTrail(originalCanvas);
        }
    #endif
    
        for (int i = 0; i <= index; i++) {
    #if SK_GPU_V1
            GrAuditTrail::AutoCollectOps* acb = nullptr;
            if (at) {
                // We need to flush any pending operations, or they might combine with commands below.
                // Previous operations were not registered with the audit trail when they were
                // created, so if we allow them to combine, the audit trail will fail to find them.
                if (dContext) {
                    dContext->flush();
                }
                acb = new GrAuditTrail::AutoCollectOps(at, i);
            }
    #endif
            if (fCommandVector[i]->isVisible()) {
                fCommandVector[i]->execute(finalCanvas);
            }
    #if SK_GPU_V1
            if (at && acb) {
                delete acb;
            }
    #endif
        }
    
        if (SkColorGetA(fClipVizColor) != 0) {
            finalCanvas->save();
            SkPaint clipPaint;
            clipPaint.setColor(fClipVizColor);
            finalCanvas->drawPaint(clipPaint);
            finalCanvas->restore();
        }
    
        fMatrix = finalCanvas->getLocalToDevice();
        fClip   = finalCanvas->getDeviceClipBounds();
        if (fShowOrigin) {
            const SkPaint originXPaint = SkPaint({1.0, 0, 0, 1.0});
            const SkPaint originYPaint = SkPaint({0, 1.0, 0, 1.0});
            // Draw an origin cross at the origin before restoring to assist in visualizing the
            // current matrix.
            drawArrow(finalCanvas, {-50, 0}, {50, 0}, originXPaint);
            drawArrow(finalCanvas, {0, -50}, {0, 50}, originYPaint);
        }
        finalCanvas->restoreToCount(saveCount);
    
        if (fShowAndroidClip) {
            // Draw visualization of android device clip restriction
            SkPaint androidClipPaint;
            androidClipPaint.setARGB(80, 255, 100, 0);
            finalCanvas->drawRect(fAndroidClip, androidClipPaint);
        }
    
    #if SK_GPU_V1
        // draw any ops if required and issue a full reset onto GrAuditTrail
        if (at) {
            // just in case there is global reordering, we flush the canvas before querying
            // GrAuditTrail
            GrAuditTrail::AutoEnable ae(at);
            if (dContext) {
                dContext->flush();
            }
    
            // we pick three colorblind-safe colors, 75% alpha
            static const SkColor kTotalBounds     = SkColorSetARGB(0xC0, 0x6A, 0x3D, 0x9A);
            static const SkColor kCommandOpBounds = SkColorSetARGB(0xC0, 0xE3, 0x1A, 0x1C);
            static const SkColor kOtherOpBounds   = SkColorSetARGB(0xC0, 0xFF, 0x7F, 0x00);
    
            // get the render target of the top device (from the original canvas) so we can ignore ops
            // drawn offscreen
            GrRenderTargetProxy* rtp = SkCanvasPriv::TopDeviceTargetProxy(originalCanvas);
            GrSurfaceProxy::UniqueID proxyID = rtp->uniqueID();
    
            // get the bounding boxes to draw
            SkTArray<GrAuditTrail::OpInfo> childrenBounds;
            if (m == -1) {
                at->getBoundsByClientID(&childrenBounds, index);
            } else {
                // the client wants us to draw the mth op
                at->getBoundsByOpsTaskID(&childrenBounds.push_back(), m);
            }
            // Shift the rects half a pixel, so they appear as exactly 1px thick lines.
            finalCanvas->save();
            finalCanvas->translate(0.5, -0.5);
            SkPaint paint;
            paint.setStyle(SkPaint::kStroke_Style);
            paint.setStrokeWidth(1);
            for (int i = 0; i < childrenBounds.count(); i++) {
                if (childrenBounds[i].fProxyUniqueID != proxyID) {
                    // offscreen draw, ignore for now
                    continue;
                }
                paint.setColor(kTotalBounds);
                finalCanvas->drawRect(childrenBounds[i].fBounds, paint);
                for (int j = 0; j < childrenBounds[i].fOps.count(); j++) {
                    const GrAuditTrail::OpInfo::Op& op = childrenBounds[i].fOps[j];
                    if (op.fClientID != index) {
                        paint.setColor(kOtherOpBounds);
                    } else {
                        paint.setColor(kCommandOpBounds);
                    }
                    finalCanvas->drawRect(op.fBounds, paint);
                }
            }
            finalCanvas->restore();
            this->cleanupAuditTrail(at);
        }
    #endif
    }
    View Code

    third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.cc

    double y,
    double width,
    double height,
    bool for_reset
     
    Webgl初始参数:
    /**
     * Options for configuring a WebGL context. If an option is omitted, a sensible default will
     * be used. These are defined by the WebGL standards.
     */
    export interface WebGLOptions {
        alpha?: number;
        antialias?: number;
        depth?: number;
        enableExtensionsByDefault?: number;
        explicitSwapControl?: number;
        failIfMajorPerformanceCaveat?: number;
        majorVersion?: number;
        minorVersion?: number;
        preferLowPowerToHighPerformance?: number;
        premultipliedAlpha?: number;
        preserveDrawingBuffer?: number;
        renderViaOffscreenBackBuffer?: number;
        stencil?: number;
    }
    View Code

     surface 与 canvas示例:

    C:\dev\skia_source\modules\canvaskit\npm_build\multicanvas.html

    C:\dev\skia_source\modules\canvaskit\npm_build\types\canvaskit-wasm-tests.ts

    一个 surfaceTests js代码:

    function surfaceTests(CK: CanvasKit, gl?: WebGLRenderingContext) {
        if (!gl) {
            return;
        }
        const canvasEl = document.querySelector('canvas') as HTMLCanvasElement;
        const surfaceOne = CK.MakeCanvasSurface(canvasEl)!; // $ExpectType Surface
        const surfaceTwo = CK.MakeCanvasSurface('my_canvas')!;
        const surfaceThree = CK.MakeSWCanvasSurface(canvasEl)!; // $ExpectType Surface
        const surfaceFour = CK.MakeSWCanvasSurface('my_canvas')!;
        const surfaceFive = CK.MakeWebGLCanvasSurface(canvasEl, // $ExpectType Surface
            CK.ColorSpace.SRGB, {
            majorVersion: 2,
            preferLowPowerToHighPerformance: 1,
        })!;
        const surfaceSix = CK.MakeWebGLCanvasSurface('my_canvas', CK.ColorSpace.DISPLAY_P3, {
            enableExtensionsByDefault: 2,
        })!;
        const surfaceSeven = CK.MakeSurface(200, 200)!; // $ExpectType Surface
        const m = CK.Malloc(Uint8Array, 5 * 5 * 4);
        const surfaceEight = CK.MakeRasterDirectSurface({
             5,
            height: 5,
            colorType: CK.ColorType.RGBA_8888,
            alphaType: CK.AlphaType.Premul,
            colorSpace: CK.ColorSpace.SRGB,
        }, m, 20);
    
        surfaceOne.flush();
        const canvas = surfaceTwo.getCanvas(); // $ExpectType Canvas
        const ii = surfaceThree.imageInfo(); // $ExpectType ImageInfo
        const h = surfaceFour.height(); // $ExpectType number
        const w = surfaceFive.width(); // $ExpectType number
        const subsurface = surfaceOne.makeSurface(ii); // $ExpectType Surface
        const isGPU = subsurface.reportBackendTypeIsGPU(); // $ExpectType boolean
        const count = surfaceThree.sampleCnt(); // $ExpectType number
        const img = surfaceFour.makeImageSnapshot([0, 3, 2, 5]); // $ExpectType Image
        const img2 = surfaceSix.makeImageSnapshot(); // $ExpectType Image
        const img3 = surfaceFour.makeImageFromTexture(gl.createTexture()!, {
          height: 40,
           80,
          colorType: CK.ColorType.RGBA_8888,
          alphaType: CK.AlphaType.Unpremul,
          colorSpace: CK.ColorSpace.SRGB,
        });
        const img4 = surfaceFour.makeImageFromTextureSource(new Image()); // $ExpectType Image | null
        const videoEle = document.createElement('video');
        const img5 = surfaceFour.makeImageFromTextureSource(videoEle, {
          height: 40,
           80,
          colorType: CK.ColorType.RGBA_8888,
          alphaType: CK.AlphaType.Unpremul,
        });
        const img6 = surfaceFour.makeImageFromTextureSource(new ImageData(40, 80)); // $ExpectType Image | null
    
        surfaceSeven.delete();
    
        const ctx = CK.GetWebGLContext(canvasEl); // $ExpectType number
        CK.deleteContext(ctx);
        const grCtx = CK.MakeGrContext(ctx);
        const surfaceNine = CK.MakeOnScreenGLSurface(grCtx!, 100, 400, // $ExpectType Surface
            CK.ColorSpace.ADOBE_RGB)!;
    
        const rt = CK.MakeRenderTarget(grCtx!, 100, 200); // $ExpectType Surface | null
        const rt2 = CK.MakeRenderTarget(grCtx!, { // $ExpectType Surface | null
             79,
            height: 205,
            colorType: CK.ColorType.RGBA_8888,
            alphaType: CK.AlphaType.Premul,
            colorSpace: CK.ColorSpace.SRGB,
        });
    
        const drawFrame = (canvas: Canvas) => {
            canvas.clear([0, 0, 0, 0]);
        };
        surfaceFour.requestAnimationFrame(drawFrame);
        surfaceFour.drawOnce(drawFrame);
    }
    View Code

    示例,在canvas上做个imageInfo,获取到改变尺寸的surface,看看它是不是gpu。c++

    void draw(SkCanvas* canvas) {
        sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(5, 6);
        SkCanvas* smallCanvas = surface->getCanvas();
        SkImageInfo imageInfo = SkImageInfo::MakeN32Premul(10, 14);
        sk_sp<SkSurface> compatible = smallCanvas->makeSurface(imageInfo);
        SkDebugf("compatible %c= nullptr\n", compatible == nullptr ? '=' : '!');
        SkDebugf("size = %d, %d\n", compatible->width(), compatible->height());

    ???js:
    const isGPU = subsurface.reportBackendTypeIsGPU(); // $ExpectType boolean
    }

    一个给canvaskit 报告的bug 写的测试示例,这个bug已经关闭:

    let htmlCanvas;
    let skCanvas;
    let skSurface;
    const paint = new CanvasKit.Paint();
    
    function getCanvasLayer(w,h) {
      htmlCanvas = document.getElementById("canvas");
      console.log("Canvas class: %s", htmlCanvas.constructor.name);
      htmlCanvas.height = h;
      htmlCanvas.width = w;
    }
    
    function prepareSurface(w, h) {
      if (skSurface && !skSurface.isDeleted()) {
        skSurface.dispose();
        console.log('Disposed surface');
      }
      const context = htmlCanvas.getContext("2d");
      skSurface = CanvasKit.MakeWebGLCanvasSurface(htmlCanvas);
      if (!skSurface) {
        console.log('Failed to make surface');
      }
    }
    
    function drawOffscreenCanvas(skps, w,h) {
      let picture = CanvasKit.MakePicture(skps);
      skCanvas = skSurface.getCanvas();
      skCanvas.save();
      skCanvas.drawPicture(picture);
      skCanvas.restore();
      picture.delete();
    }
    
    function flushOffscreenCanvas(w,h) {
      skSurface.flush();
    
      // Here is something interesting, remove line 19 and call line 20, after MakeWebGLCanvasSurface(htmlCanvas)
      // htmlCanvas.getContext("2d") returns null context.
      // htmlCanvas.getContext("webgl") returns null context.
      // htmlCanvas.getContext("webgl2") return a valid WebGL2RenderingContext.
      // Now if we move this getContext before MakeWebGLCanvasSurface, all 3
      // context "2d", "webgl" and "webgl2" return a valid context but 
      // MakeWebGLCanvasSurface will throw an error:
      // Uncaught (in promise) TypeError: Cannot read property 'version' of undefined
    
      //const context = htmlCanvas.getContext("webgl");
      //console.log("Context class: %s", context.constructor.name);
    }
    
    function drawFrame(skps) {
      const canvasLayerWidth = 3000;
      const canvasLayerHeight = 3000;
      const w = 1000;
      const h = 1000;
      getCanvasLayer(canvasLayerWidth,canvasLayerHeight);
      prepareSurface(w,h);
      drawOffscreenCanvas(skps,w,h);
      flushOffscreenCanvas(w,h); 
    }
    
    fetch('url', {
      'mode': 'cors'
    })
      .then(response => response.blob())
      .then(blob => blob.arrayBuffer())
      .then(skpic => {
          console.log(skpic)
          drawFrame(skpic);
      });
    View Code

     js canvas 动画示例:都是官方网站的:

    Canvas api:包括transform,video:https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API

    https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing

    https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Manipulating_video_using_canvas

    https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations

    3d迷宫移动:https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/A_basic_ray-caster

    带球:https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Advanced_animations


    1, Home » Porting » Connecting C++ and JavaScript 

  • 相关阅读:
    多线程
    Java I/O简述
    Java集合
    Java常用类
    Java面向对象基础知识汇总
    Java标识符和关键字
    认识Java
    mvn打包源码和生成javadoc
    Http协议概述
    exe可执行程序及堆栈分配(转载)
  • 原文地址:https://www.cnblogs.com/bigben0123/p/15988053.html
Copyright © 2020-2023  润新知