CPU image is with
drawString
x, y
GPU image is with translate x y | drawString 0, 0
```cpp
bool GPU = false;
void drawText(SkCanvas * canvas, int n, int x, int y)
{
 auto paint = SkPaint();
 paint.setColor(SK_ColorWHITE);
 for (int i = 0; i < n; i++)
 {
  std::string text = "drawn ";
  text += std::to_string(n);
  text += " time";
  if (i != 0) text += "s";
  if (!GPU)
  {
    canvas->drawString(text.c_str(), x, y, SkFont(nullptr, 12), paint);
  }
  else
  {
   canvas->save();
   canvas->translate(x, y);
   canvas->drawString(text.c_str(), 0, 0, SkFont(nullptr, 12), paint);
   canvas->restore();
  }
 }
}
void drawMatrix(SkCanvas * canvas, int count, int max_lines, int spacing)
{
 max_lines++;
 int n = 0;
 int column = 0;
 int line = 1;
 for (int i = 0; i < count; i++)
 {
  n = i + 1;
  if (line == max_lines)
  {
   line = 1;
   column += spacing;
  }
  drawText(canvas, n, column, 20 * line);
  line++;
 }
}
void draw(SkCanvas* canvas) {
 auto txtpaint = SkPaint();
 txtpaint.setColor(SK_ColorWHITE);
Â
 canvas->drawColor(SK_ColorBLACK);
Â
 auto sksl = SkRuntimeEffect::MakeForShader(
  SkString(
  "uniform shader input_;\n"
  "uniform shader inputAlpha;\n"
  "\n"
  "half4 main(float2 cord) {\n"
  "   half4 color = input_.eval(cord);\n"
  // return zero if alpha is zero
  "   if (color.a == 0) return vec4(0,0,0,1);\n"
  "   int alpha = (int)(255.0 * inputAlpha.eval(cord).a);\n"
  // return color if input alpha is 0, this means we only drawn this pixel once
  // Skia's overdraw canvas increases the alpha of a pixel each time it drawn touched
  // R G B A
  "   if (alpha == 0) {\n"
  // apply greyscale to the overdraw canvas in order to isolate the overdraw colors
  "    return half4(vec3((color.r + color.g + color.b) / 3), 1);\n"
  "   }\n"
  "   return half4(1,0,0,1);\n"
  "}\n")
 );
 if (sksl.effect == nullptr) {
  auto text = sksl.errorText;
  auto text_c = text.c_str();
  SkDebugf("SHADER ERROR!\n");
  SkDebugf("%s\n", text_c);
  canvas->drawString("SHADER ERROR!", 20, 40, SkFont(nullptr, 12), txtpaint);
  canvas->drawString(text_c, 0, 52, SkFont(nullptr, 12), txtpaint);
  return;
 }
 auto surface = canvas->getSurface();
 GrRecordingContext * context = surface ? surface->recordingContext() : nullptr;
 GPU = surface && context;
Â
 int w = canvas->imageInfo().width();
 int h = canvas->imageInfo().height();
Â
 auto offscreenInfo = canvas->imageInfo().makeWH(w, h);
 auto offscreenAlphaInfo = SkImageInfo::MakeA8(w, h);
Â
 auto offscreenSurface =
  GPU ? SkSurface::MakeRenderTarget(canvas->getSurface()->recordingContext(), SkBudgeted::kNo, offscreenInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr)
   : SkSurface::MakeRaster(offscreenInfo, kTopLeft_GrSurfaceOrigin, nullptr);
 auto offscreenAlphaSurface =
  GPU ? SkSurface::MakeRenderTarget(canvas->getSurface()->recordingContext(), SkBudgeted::kNo, offscreenAlphaInfo, 0, kTopLeft_GrSurfaceOrigin, nullptr)
   : SkSurface::MakeRaster(offscreenAlphaInfo, kTopLeft_GrSurfaceOrigin, nullptr);
Â
 if (!offscreenSurface || !offscreenAlphaSurface) {
  SkDebugf("SURFACE ERROR!\n");
  canvas->drawString("SURFACE ERROR!", 20, 40, SkFont(nullptr, 12), txtpaint);
  return;
 }
Â
 auto imageCanvas = offscreenSurface->getCanvas();
 auto alphaCnvas = offscreenAlphaSurface->getCanvas();
Â
 if (!imageCanvas || !alphaCnvas) {
  SkDebugf("CANVAS ERROR!\n");
  canvas->drawString("CANVAS ERROR!", 20, 40, SkFont(nullptr, 12), txtpaint);
  return;
 }
Â
 auto overdrawCanvas = SkOverdrawCanvas(alphaCnvas);
 auto nWayCanvas = SkNWayCanvas(w, h);
 nWayCanvas.addCanvas(&overdrawCanvas);
 nWayCanvas.addCanvas(imageCanvas);
Â
 drawMatrix(&nWayCanvas, 20, 20, 50);
Â
 nWayCanvas.flush();
Â
 auto imageAlpha = offscreenAlphaSurface->makeImageSnapshot();
 auto image_ = offscreenSurface->makeImageSnapshot();
Â
 if (!image_ || !imageAlpha) {
  SkDebugf("SNAPSHOT ERROR!\n");
  canvas->drawString("SNAPSHOT ERROR!", 20, 40, SkFont(nullptr, 12), txtpaint);
  return;
 }
Â
 auto imageAlphaShader = imageAlpha->makeShader(SkSamplingOptions());
 auto imageShader = image_->makeShader(SkSamplingOptions());
Â
 if (!imageAlphaShader || !imageShader) {
  SkDebugf("MAKE SHADER ERROR!\n");
  canvas->drawString("MAKE SHADER ERROR!", 20, 40, SkFont(nullptr, 12), txtpaint);
  return;
 }
 SkRuntimeShaderBuilder builder(sksl.effect);
 builder.child("input_") = imageShader;
 builder.child("inputAlpha") = imageAlphaShader;
Â
 auto ourShader = builder.makeShader();
Â
 if (!ourShader) {
  SkDebugf("COMBINE SHADER ERROR!\n");
  canvas->drawString("COMBINE SHADER ERROR!", 20, 40, SkFont(nullptr, 12), txtpaint);
  return;
 }
Â
 SkPaint paint;
 paint.setBlendMode(SkBlendMode::kSrc);
 paint.setShader(ourShader);
 canvas->drawPaint(paint);
Â
 SkDebugf("DRAWN!\n");
}
```