blob: 54bf06734cb9cb978affc953ba530c350723d330 [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkAlphaType.h"
#include "include/core/SkBlender.h"
#include "include/core/SkColor.h"
#include "include/core/SkMaskFilter.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRSXform.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkShader.h"
#include "include/core/SkSurfaceProps.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkBlendModePriv.h"
#include "src/core/SkBlenderBase.h"
#include "src/core/SkColorSpacePriv.h"
#include "src/core/SkColorSpaceXformSteps.h"
#include "src/core/SkCoreBlitters.h"
#include "src/core/SkDraw.h"
#include "src/core/SkEffectPriv.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkRasterClip.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkRasterPipelineOpContexts.h"
#include "src/core/SkRasterPipelineOpList.h"
#include "src/core/SkScan.h"
#include "src/core/SkSurfacePriv.h"
#include "src/core/SkVM.h"
#include "src/core/SkVMBlitter.h"
#include "src/shaders/SkShaderBase.h"
#include "src/shaders/SkTransformShader.h"
#include <cstdint>
#include <optional>
#include <utility>
class SkBlitter;
class SkColorInfo;
class SkColorSpace;
enum class SkBlendMode;
static void fill_rect(const SkMatrix& ctm, const SkRasterClip& rc,
const SkRect& r, SkBlitter* blitter, SkPath* scratchPath) {
if (ctm.rectStaysRect()) {
SkRect dr;
ctm.mapRect(&dr, r);
SkScan::FillRect(dr, rc, blitter);
} else {
SkPoint pts[4];
r.toQuad(pts);
ctm.mapPoints(pts, pts, 4);
scratchPath->rewind();
scratchPath->addPoly(pts, 4, true);
SkScan::FillPath(*scratchPath, rc, blitter);
}
}
static void load_color(SkRasterPipeline_UniformColorCtx* ctx, const float rgba[]) {
// only need one of these. can I query the pipeline to know if its lowp or highp?
ctx->rgba[0] = SkScalarRoundToInt(rgba[0]*255); ctx->r = rgba[0];
ctx->rgba[1] = SkScalarRoundToInt(rgba[1]*255); ctx->g = rgba[1];
ctx->rgba[2] = SkScalarRoundToInt(rgba[2]*255); ctx->b = rgba[2];
ctx->rgba[3] = SkScalarRoundToInt(rgba[3]*255); ctx->a = rgba[3];
}
extern bool gUseSkVMBlitter;
class UpdatableColorShader : public SkShaderBase {
public:
explicit UpdatableColorShader(SkColorSpace* cs)
: fSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType, cs, kUnpremul_SkAlphaType} {}
skvm::Color program(skvm::Builder* builder,
skvm::Coord device,
skvm::Coord local,
skvm::Color paint,
const MatrixRec&,
const SkColorInfo& dst,
skvm::Uniforms* uniforms,
SkArenaAlloc* alloc) const override {
skvm::Uniform color = uniforms->pushPtr(fValues);
skvm::F32 r = builder->arrayF(color, 0);
skvm::F32 g = builder->arrayF(color, 1);
skvm::F32 b = builder->arrayF(color, 2);
skvm::F32 a = builder->arrayF(color, 3);
return {r, g, b, a};
}
void updateColor(SkColor c) const {
SkColor4f c4 = SkColor4f::FromColor(c);
fSteps.apply(c4.vec());
auto cp4 = c4.premul();
fValues[0] = cp4.fR;
fValues[1] = cp4.fG;
fValues[2] = cp4.fB;
fValues[3] = cp4.fA;
}
private:
// For serialization. This will never be called.
Factory getFactory() const override { return nullptr; }
const char* getTypeName() const override { return nullptr; }
SkColorSpaceXformSteps fSteps;
mutable float fValues[4];
};
void SkDraw::drawAtlas(const SkRSXform xform[],
const SkRect textures[],
const SkColor colors[],
int count,
sk_sp<SkBlender> blender,
const SkPaint& paint) {
sk_sp<SkShader> atlasShader = paint.refShader();
if (!atlasShader) {
return;
}
SkSTArenaAlloc<256> alloc;
SkPaint p(paint);
p.setAntiAlias(false); // we never respect this for drawAtlas(or drawVertices)
p.setStyle(SkPaint::kFill_Style);
p.setShader(nullptr);
p.setMaskFilter(nullptr);
const SkMatrix& ctm = fMatrixProvider->localToDevice();
// The RSXForms can't contain perspective - only the CTM cab.
const bool perspective = ctm.hasPerspective();
auto transformShader = alloc.make<SkTransformShader>(*as_SB(atlasShader), perspective);
auto rpblit = [&]() {
SkRasterPipeline pipeline(&alloc);
SkSurfaceProps props = SkSurfacePropsCopyOrDefault(fProps);
SkStageRec rec = {
&pipeline, &alloc, fDst.colorType(), fDst.colorSpace(), p.getColor4f(), props};
// We pass an identity matrix here rather than the CTM. The CTM gets folded into the
// per-triangle matrix.
if (!as_SB(transformShader)->appendRootStages(rec, SkMatrix::I())) {
return false;
}
SkRasterPipeline_UniformColorCtx* uniformCtx = nullptr;
SkColorSpaceXformSteps steps(
sk_srgb_singleton(), kUnpremul_SkAlphaType, rec.fDstCS, kUnpremul_SkAlphaType);
if (colors) {
// we will late-bind the values in ctx, once for each color in the loop
uniformCtx = alloc.make<SkRasterPipeline_UniformColorCtx>();
rec.fPipeline->append(SkRasterPipelineOp::uniform_color_dst, uniformCtx);
if (std::optional<SkBlendMode> bm = as_BB(blender)->asBlendMode(); bm.has_value()) {
SkBlendMode_AppendStages(*bm, rec.fPipeline);
} else {
return false;
}
}
bool isOpaque = !colors && transformShader->isOpaque();
if (p.getAlphaf() != 1) {
rec.fPipeline->append(SkRasterPipelineOp::scale_1_float,
alloc.make<float>(p.getAlphaf()));
isOpaque = false;
}
auto blitter = SkCreateRasterPipelineBlitter(
fDst, p, pipeline, isOpaque, &alloc, fRC->clipShader());
if (!blitter) {
return false;
}
SkPath scratchPath;
for (int i = 0; i < count; ++i) {
if (colors) {
SkColor4f c4 = SkColor4f::FromColor(colors[i]);
steps.apply(c4.vec());
load_color(uniformCtx, c4.premul().vec());
}
SkMatrix mx;
mx.setRSXform(xform[i]);
mx.preTranslate(-textures[i].fLeft, -textures[i].fTop);
mx.postConcat(ctm);
if (transformShader->update(mx)) {
fill_rect(mx, *fRC, textures[i], blitter, &scratchPath);
}
}
return true;
};
if (gUseSkVMBlitter || !rpblit()) {
UpdatableColorShader* colorShader = nullptr;
sk_sp<SkShader> shader;
if (colors) {
colorShader = alloc.make<UpdatableColorShader>(fDst.colorSpace());
shader = SkShaders::Blend(std::move(blender),
sk_ref_sp(colorShader),
sk_ref_sp(transformShader));
} else {
shader = sk_ref_sp(transformShader);
}
p.setShader(std::move(shader));
// We use identity here and fold the CTM into the update matrix.
if (auto blitter = SkVMBlitter::Make(fDst,
p,
SkMatrix::I(),
&alloc,
fRC->clipShader())) {
SkPath scratchPath;
for (int i = 0; i < count; ++i) {
if (colorShader) {
colorShader->updateColor(colors[i]);
}
SkMatrix mx;
mx.setRSXform(xform[i]);
mx.preTranslate(-textures[i].fLeft, -textures[i].fTop);
mx.postConcat(ctm);
if (transformShader->update(mx)) {
fill_rect(mx, *fRC, textures[i], blitter, &scratchPath);
}
}
}
}
}