Add Atlas Text interface for rendering SDF glyphs.
This new API is built upon SDF text atlas code from the GPU backend. Unlike using the GPU backend to draw text, this set of interfaces allows the client to render the SDF glyphs. The client issues text draws to potentially multiple targets and then the client flushes. The client then gets commands from Skia with data to put into a texture atlas and vertices to draw that reference the texture. The client is responsible for creating the texture, uploading the SDF data to the texture, and drawing the vertices provided by Skia. Change-Id: Ie9447e19b85f0ce1c2b942e5216c787a74f335d3 Reviewed-on: https://skia-review.googlesource.com/59360 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
e686cc4efa
commit
39631f3df1
13
BUILD.gn
13
BUILD.gn
@ -52,6 +52,7 @@ declare_args() {
|
||||
declare_args() {
|
||||
skia_use_dng_sdk = !is_fuchsia && skia_use_libjpeg_turbo && skia_use_zlib
|
||||
skia_use_sfntly = skia_use_icu
|
||||
skia_enable_atlas_text = is_skia_dev_build && skia_enable_gpu
|
||||
|
||||
if (is_android) {
|
||||
skia_use_vulkan = defined(ndk_api) && ndk_api >= 24
|
||||
@ -92,6 +93,7 @@ skia_public_includes = [
|
||||
"include/encode",
|
||||
"include/gpu",
|
||||
"include/gpu/gl",
|
||||
"include/atlastext",
|
||||
"include/pathops",
|
||||
"include/ports",
|
||||
"include/svg",
|
||||
@ -102,6 +104,9 @@ skia_public_includes = [
|
||||
if (skia_use_vulkan) {
|
||||
skia_public_includes += [ "include/gpu/vk" ]
|
||||
}
|
||||
if (skia_enable_atlas_text) {
|
||||
skia_public_includes += [ "include/atlastext" ]
|
||||
}
|
||||
if (skia_use_metal) {
|
||||
skia_public_includes += [ "include/gpu/mtl" ]
|
||||
}
|
||||
@ -125,6 +130,9 @@ config("skia_public") {
|
||||
if (!skia_enable_gpu) {
|
||||
defines += [ "SK_SUPPORT_GPU=0" ]
|
||||
}
|
||||
if (skia_enable_atlas_text) {
|
||||
defines += [ "SK_SUPPORT_ATLAS_TEXT=1" ]
|
||||
}
|
||||
}
|
||||
|
||||
# Skia internal APIs, used by Skia itself and a few test tools.
|
||||
@ -586,6 +594,10 @@ optional("gpu") {
|
||||
libs += [ "Metal.framework" ]
|
||||
cflags_objcc += [ "-fobjc-arc" ]
|
||||
}
|
||||
|
||||
if (skia_enable_atlas_text) {
|
||||
sources += skia_atlas_text_sources
|
||||
}
|
||||
}
|
||||
|
||||
optional("heif") {
|
||||
@ -992,6 +1004,7 @@ if (skia_enable_tools) {
|
||||
"tools/gpu/GrContextFactory.cpp",
|
||||
"tools/gpu/GrTest.cpp",
|
||||
"tools/gpu/TestContext.cpp",
|
||||
"tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp",
|
||||
"tools/gpu/gl/GLTestContext.cpp",
|
||||
"tools/gpu/gl/command_buffer/GLTestContext_command_buffer.cpp",
|
||||
"tools/gpu/gl/debug/DebugGLTestContext.cpp",
|
||||
|
123
gm/atlastext.cpp
Normal file
123
gm/atlastext.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
|
||||
#if SK_SUPPORT_ATLAS_TEXT
|
||||
|
||||
#include "SkAtlasTextContext.h"
|
||||
#include "SkAtlasTextFont.h"
|
||||
#include "SkAtlasTextTarget.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkTypeface.h"
|
||||
#include "gpu/TestContext.h"
|
||||
#include "gpu/atlastext/GLTestAtlasTextRenderer.h"
|
||||
#include "gpu/atlastext/TestAtlasTextRenderer.h"
|
||||
#include "sk_tool_utils.h"
|
||||
|
||||
// GM that draws text using the Atlas Text interface offscreen and then blits that to the canvas.
|
||||
|
||||
static SkScalar draw_string(SkAtlasTextTarget* target, const SkString& text, SkScalar x, SkScalar y,
|
||||
uint32_t color, sk_sp<SkTypeface> typeface, float size) {
|
||||
auto font = SkAtlasTextFont::Make(std::move(typeface), size);
|
||||
target->drawText(text.c_str(), text.size(), x, y, color, *font);
|
||||
SkPaint paint;
|
||||
paint.setTextSize(size);
|
||||
return x + paint.measureText(text.c_str(), text.size());
|
||||
}
|
||||
|
||||
class AtlasTextGM : public skiagm::GM {
|
||||
public:
|
||||
AtlasTextGM() = default;
|
||||
|
||||
protected:
|
||||
SkString onShortName() override { return SkString("atlastext"); }
|
||||
|
||||
SkISize onISize() override { return SkISize::Make(kSize, kSize); }
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
fRenderer = sk_gpu_test::MakeGLTestAtlasTextRenderer();
|
||||
if (!fRenderer) {
|
||||
return;
|
||||
}
|
||||
fContext = SkAtlasTextContext::Make(fRenderer);
|
||||
auto targetHandle = fRenderer->makeTargetHandle(kSize, kSize);
|
||||
fTarget = SkAtlasTextTarget::Make(fContext, kSize, kSize, targetHandle);
|
||||
|
||||
fTypefaces[0] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Italic());
|
||||
fTypefaces[1] =
|
||||
sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Italic());
|
||||
fTypefaces[2] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Normal());
|
||||
fTypefaces[3] =
|
||||
sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Normal());
|
||||
fTypefaces[4] = sk_tool_utils::create_portable_typeface("serif", SkFontStyle::Bold());
|
||||
fTypefaces[5] = sk_tool_utils::create_portable_typeface("sans-serif", SkFontStyle::Bold());
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
if (!fRenderer) {
|
||||
canvas->clear(SK_ColorRED);
|
||||
return;
|
||||
}
|
||||
auto bmp = this->drawText();
|
||||
SkPaint paint;
|
||||
paint.setBlendMode(SkBlendMode::kSrc);
|
||||
canvas->drawBitmap(bmp, 0, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
SkBitmap drawText() {
|
||||
static const int kSizes[] = {8, 13, 18, 23, 30};
|
||||
|
||||
static const SkString kTexts[] = {SkString("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
|
||||
SkString("abcdefghijklmnopqrstuvwxyz"),
|
||||
SkString("0123456789"),
|
||||
SkString("!@#$%^&*()<>[]{}")};
|
||||
SkScalar x = 0;
|
||||
SkScalar y = 10;
|
||||
|
||||
SkRandom random;
|
||||
do {
|
||||
for (auto s : kSizes) {
|
||||
auto size = 2 * s;
|
||||
for (const auto& typeface : fTypefaces) {
|
||||
for (const auto& text : kTexts) {
|
||||
uint32_t color = random.nextU();
|
||||
x = size + draw_string(fTarget.get(), text, x, y, color, typeface, size);
|
||||
x = SkScalarCeilToScalar(x);
|
||||
if (x + 100 > kSize) {
|
||||
x = 0;
|
||||
y += SkScalarCeilToScalar(size + 3);
|
||||
if (y > kSize) {
|
||||
fTarget->flush();
|
||||
return fRenderer->readTargetHandle(fTarget->handle());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
static constexpr int kSize = 1280;
|
||||
|
||||
sk_sp<SkTypeface> fTypefaces[6];
|
||||
sk_sp<sk_gpu_test::TestAtlasTextRenderer> fRenderer;
|
||||
std::unique_ptr<SkAtlasTextTarget> fTarget;
|
||||
sk_sp<SkAtlasTextContext> fContext;
|
||||
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
constexpr int AtlasTextGM::kSize;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DEF_GM(return new AtlasTextGM;)
|
||||
|
||||
#endif
|
@ -134,6 +134,7 @@ skia_core_sources = [
|
||||
"$_src/core/SkFindAndPlaceGlyph.h",
|
||||
"$_src/core/SkArenaAlloc.cpp",
|
||||
"$_src/core/SkArenaAlloc.h",
|
||||
"$_src/core/SkArenaAllocList.h",
|
||||
"$_src/core/SkGaussFilter.cpp",
|
||||
"$_src/core/SkGaussFilter.h",
|
||||
"$_src/core/SkFlattenable.cpp",
|
||||
|
@ -22,6 +22,7 @@ gm_sources = [
|
||||
"$_gm/arcofzorro.cpp",
|
||||
"$_gm/arcto.cpp",
|
||||
"$_gm/arithmode.cpp",
|
||||
"$_gm/atlastext.cpp",
|
||||
"$_gm/badpaint.cpp",
|
||||
"$_gm/beziereffects.cpp",
|
||||
"$_gm/beziers.cpp",
|
||||
|
11
gn/gpu.gni
11
gn/gpu.gni
@ -628,3 +628,14 @@ skia_native_gpu_sources = [
|
||||
"$_src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp",
|
||||
"$_src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp",
|
||||
]
|
||||
|
||||
skia_atlas_text_sources = [
|
||||
"$_include/atlastext/SkAtlasTextContext.h",
|
||||
"$_include/atlastext/SkAtlasTextFont.h",
|
||||
"$_include/atlastext/SkAtlasTextRenderer.h",
|
||||
"$_include/atlastext/SkAtlasTextTarget.h",
|
||||
|
||||
"$_src/atlastext/SkAtlasTextContext.cpp",
|
||||
"$_src/atlastext/SkAtlasTextTarget.cpp",
|
||||
"$_src/atlastext/SkInternalAtlasTextContext.cpp",
|
||||
]
|
||||
|
42
include/atlastext/SkAtlasTextContext.h
Normal file
42
include/atlastext/SkAtlasTextContext.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkAtlasTextContext_DEFINED
|
||||
#define SkAtlasTextContext_DEFINED
|
||||
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
class SkAtlasTextRenderer;
|
||||
class SkInternalAtlasTextContext;
|
||||
|
||||
SkAtlasTextRenderer* SkGetAtlasTextRendererFromInternalContext(class SkInternalAtlasTextContext&);
|
||||
|
||||
/**
|
||||
* Class that Atlas Text client uses to register their SkAtlasTextRenderer implementation and
|
||||
* to create one or more SkAtlasTextTargets (destination surfaces for text rendering).
|
||||
*/
|
||||
class SK_API SkAtlasTextContext : public SkRefCnt {
|
||||
public:
|
||||
static sk_sp<SkAtlasTextContext> Make(sk_sp<SkAtlasTextRenderer>);
|
||||
|
||||
SkAtlasTextRenderer* renderer() const {
|
||||
return SkGetAtlasTextRendererFromInternalContext(*fInternalContext);
|
||||
}
|
||||
|
||||
SkInternalAtlasTextContext& internal() { return *fInternalContext; }
|
||||
|
||||
private:
|
||||
SkAtlasTextContext() = delete;
|
||||
SkAtlasTextContext(const SkAtlasTextContext&) = delete;
|
||||
SkAtlasTextContext& operator=(const SkAtlasTextContext&) = delete;
|
||||
|
||||
SkAtlasTextContext(sk_sp<SkAtlasTextRenderer>);
|
||||
|
||||
std::unique_ptr<SkInternalAtlasTextContext> fInternalContext;
|
||||
};
|
||||
|
||||
#endif
|
35
include/atlastext/SkAtlasTextFont.h
Normal file
35
include/atlastext/SkAtlasTextFont.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkAtlasTextFont_DEFINED
|
||||
#define SkAtlasTextFont_DEFINED
|
||||
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkTypeface.h"
|
||||
|
||||
/** Represents a font at a size. TODO: What else do we need here (skewX, scaleX, vertical, ...)? */
|
||||
class SK_API SkAtlasTextFont : public SkRefCnt {
|
||||
public:
|
||||
static sk_sp<SkAtlasTextFont> Make(sk_sp<SkTypeface> typeface, SkScalar size) {
|
||||
return sk_sp<SkAtlasTextFont>(new SkAtlasTextFont(std::move(typeface), size));
|
||||
}
|
||||
|
||||
SkTypeface* typeface() const { return fTypeface.get(); }
|
||||
|
||||
sk_sp<SkTypeface> refTypeface() const { return fTypeface; }
|
||||
|
||||
SkScalar size() const { return fSize; }
|
||||
|
||||
private:
|
||||
SkAtlasTextFont(sk_sp<SkTypeface> typeface, SkScalar size)
|
||||
: fTypeface(std::move(typeface)), fSize(size) {}
|
||||
|
||||
sk_sp<SkTypeface> fTypeface;
|
||||
SkScalar fSize;
|
||||
};
|
||||
|
||||
#endif
|
71
include/atlastext/SkAtlasTextRenderer.h
Normal file
71
include/atlastext/SkAtlasTextRenderer.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkPoint.h"
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
#ifndef SkAtlasTextRenderer_DEFINED
|
||||
#define SkAtlasTextRenderer_DEFINED
|
||||
|
||||
/**
|
||||
* This is the base class for a renderer implemented by the SkAtlasText client. The
|
||||
* SkAtlasTextContext issues texture creations, deletions, uploads, and vertex draws to the
|
||||
* renderer. The renderer must perform those actions in the order called to correctly render
|
||||
* the text drawn to SkAtlasTextTargets.
|
||||
*/
|
||||
class SK_API SkAtlasTextRenderer : public SkRefCnt {
|
||||
public:
|
||||
enum class AtlasFormat {
|
||||
/** Unsigned normalized 8 bit single channel format. */
|
||||
kA8
|
||||
};
|
||||
|
||||
struct SDFVertex {
|
||||
/** Position in device space (not normalized). */
|
||||
SkPoint fPosition;
|
||||
/** Color, same value for all four corners of a glyph quad. */
|
||||
uint32_t fColor;
|
||||
/** Texture coordinate (in texel units, not normalized). */
|
||||
SkIPoint16 fTextureCoord;
|
||||
};
|
||||
|
||||
virtual ~SkAtlasTextRenderer() = default;
|
||||
|
||||
/**
|
||||
* Create a texture of the provided format with dimensions 'width' x 'height'
|
||||
* and return a unique handle.
|
||||
*/
|
||||
virtual void* createTexture(AtlasFormat, int width, int height) = 0;
|
||||
|
||||
/**
|
||||
* Delete the texture with the passed handle.
|
||||
*/
|
||||
virtual void deleteTexture(void* textureHandle) = 0;
|
||||
|
||||
/**
|
||||
* Place the pixel data specified by 'data' in the texture with handle
|
||||
* 'textureHandle' in the rectangle ['x', 'x' + 'width') x ['y', 'y' + 'height').
|
||||
* 'rowBytes' specifies the byte offset between successive rows in 'data' and will always be
|
||||
* a multiple of the number of bytes per pixel.
|
||||
* The pixel format of data is the same as that of 'textureHandle'.
|
||||
*/
|
||||
virtual void setTextureData(void* textureHandle, const void* data, int x, int y, int width,
|
||||
int height, size_t rowBytes) = 0;
|
||||
|
||||
/**
|
||||
* Draws glyphs using SDFs. The SDF data resides in 'textureHandle'. The array
|
||||
* 'vertices' provides interleaved device-space positions, colors, and
|
||||
* texture coordinates. There are are 4 * 'quadCnt' entries in 'vertices'.
|
||||
*/
|
||||
virtual void drawSDFGlyphs(void* targetHandle, void* textureHandle, const SDFVertex vertices[],
|
||||
int quadCnt) = 0;
|
||||
|
||||
/** Called when a SkAtlasTextureTarget is destroyed. */
|
||||
virtual void targetDeleted(void* targetHandle) = 0;
|
||||
};
|
||||
|
||||
#endif
|
61
include/atlastext/SkAtlasTextTarget.h
Normal file
61
include/atlastext/SkAtlasTextTarget.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkAtlasTextTarget_DEFINED
|
||||
#define SkAtlasTextTarget_DEFINED
|
||||
|
||||
#include <memory>
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkScalar.h"
|
||||
|
||||
class SkAtlasTextContext;
|
||||
class SkAtlasTextFont;
|
||||
|
||||
/** Represents a client-created renderable surface and is used to draw text into the surface. */
|
||||
class SK_API SkAtlasTextTarget {
|
||||
public:
|
||||
virtual ~SkAtlasTextTarget();
|
||||
|
||||
/**
|
||||
* Creates a text drawing target. ‘handle’ is used to identify this rendering surface when
|
||||
* draws are flushed to the SkAtlasTextContext's SkAtlasTextRenderer.
|
||||
*/
|
||||
static std::unique_ptr<SkAtlasTextTarget> Make(sk_sp<SkAtlasTextContext>, int width, int height,
|
||||
void* handle);
|
||||
|
||||
/**
|
||||
* Enqueues a text draw in the target. The meaning of 'color' here is interpreted by the
|
||||
* client's SkAtlasTextRenderer when it actually renders the text.
|
||||
*/
|
||||
virtual void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
|
||||
uint32_t color, const SkAtlasTextFont& font) = 0;
|
||||
|
||||
/** Issues all queued text draws to SkAtlasTextRenderer. */
|
||||
virtual void flush() = 0;
|
||||
|
||||
int width() const { return fWidth; }
|
||||
int height() const { return fHeight; }
|
||||
|
||||
void* handle() const { return fHandle; }
|
||||
|
||||
SkAtlasTextContext* context() const { return fContext.get(); }
|
||||
|
||||
protected:
|
||||
SkAtlasTextTarget(sk_sp<SkAtlasTextContext>, int width, int height, void* handle);
|
||||
|
||||
void* const fHandle;
|
||||
const sk_sp<SkAtlasTextContext> fContext;
|
||||
const int fWidth;
|
||||
const int fHeight;
|
||||
|
||||
private:
|
||||
SkAtlasTextTarget() = delete;
|
||||
SkAtlasTextTarget(const SkAtlasTextContext&) = delete;
|
||||
SkAtlasTextTarget& operator=(const SkAtlasTextContext&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
@ -89,6 +89,12 @@
|
||||
# define SK_SUPPORT_GPU 1
|
||||
#endif
|
||||
|
||||
#if !defined(SK_SUPPORT_ATLAS_TEXT)
|
||||
# define SK_SUPPORT_ATLAS_TEXT 0
|
||||
#elif SK_SUPPORT_ATLAS_TEXT && !SK_SUPPORT_GPU
|
||||
# error "SK_SUPPORT_ATLAS_TEXT requires SK_SUPPORT_GPU"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The clang static analyzer likes to know that when the program is not
|
||||
* expected to continue (crash, assertion failure, etc). It will notice that
|
||||
|
17
src/atlastext/SkAtlasTextContext.cpp
Normal file
17
src/atlastext/SkAtlasTextContext.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkAtlasTextContext.h"
|
||||
#include "SkAtlasTextRenderer.h"
|
||||
#include "SkInternalAtlasTextContext.h"
|
||||
|
||||
sk_sp<SkAtlasTextContext> SkAtlasTextContext::Make(sk_sp<SkAtlasTextRenderer> renderer) {
|
||||
return sk_sp<SkAtlasTextContext>(new SkAtlasTextContext(std::move(renderer)));
|
||||
}
|
||||
|
||||
SkAtlasTextContext::SkAtlasTextContext(sk_sp<SkAtlasTextRenderer> renderer)
|
||||
: fInternalContext(SkInternalAtlasTextContext::Make(std::move(renderer))) {}
|
143
src/atlastext/SkAtlasTextTarget.cpp
Normal file
143
src/atlastext/SkAtlasTextTarget.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkAtlasTextTarget.h"
|
||||
#include "GrClip.h"
|
||||
#include "SkAtlasTextContext.h"
|
||||
#include "SkAtlasTextFont.h"
|
||||
#include "SkAtlasTextRenderer.h"
|
||||
#include "SkGr.h"
|
||||
#include "SkInternalAtlasTextContext.h"
|
||||
#include "ops/GrAtlasTextOp.h"
|
||||
#include "text/GrAtlasTextContext.h"
|
||||
|
||||
SkAtlasTextTarget::SkAtlasTextTarget(sk_sp<SkAtlasTextContext> context, int width, int height,
|
||||
void* handle)
|
||||
: fHandle(handle), fContext(std::move(context)), fWidth(width), fHeight(height) {}
|
||||
|
||||
SkAtlasTextTarget::~SkAtlasTextTarget() { fContext->renderer()->targetDeleted(fHandle); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const GrColorSpaceInfo kColorSpaceInfo(nullptr, kRGBA_8888_GrPixelConfig);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class SkInternalAtlasTextTarget : public GrTextUtils::Target, public SkAtlasTextTarget {
|
||||
public:
|
||||
SkInternalAtlasTextTarget(sk_sp<SkAtlasTextContext> context, int width, int height,
|
||||
void* handle)
|
||||
: GrTextUtils::Target(width, height, kColorSpaceInfo)
|
||||
, SkAtlasTextTarget(std::move(context), width, height, handle) {}
|
||||
|
||||
/** GrTextUtils::Target overrides */
|
||||
|
||||
void addDrawOp(const GrClip&, std::unique_ptr<GrAtlasTextOp> op) override;
|
||||
|
||||
void drawPath(const GrClip&, const SkPath&, const SkPaint&, const SkMatrix& viewMatrix,
|
||||
const SkMatrix* pathMatrix, const SkIRect& clipBounds) override {
|
||||
SkDebugf("Path glyph??");
|
||||
}
|
||||
|
||||
void makeGrPaint(GrMaskFormat, const SkPaint& skPaint, const SkMatrix&,
|
||||
GrPaint* grPaint) override {
|
||||
grPaint->setColor4f(SkColorToPremulGrColor4fLegacy(skPaint.getColor()));
|
||||
}
|
||||
|
||||
/** SkAtlasTextTarget overrides */
|
||||
|
||||
void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, uint32_t color,
|
||||
const SkAtlasTextFont&) override;
|
||||
void flush() override;
|
||||
|
||||
private:
|
||||
uint32_t fColor;
|
||||
using SkAtlasTextTarget::fWidth;
|
||||
using SkAtlasTextTarget::fHeight;
|
||||
struct RecordedOp {
|
||||
std::unique_ptr<GrAtlasTextOp> fOp;
|
||||
uint32_t fColor;
|
||||
};
|
||||
SkTArray<RecordedOp, true> fOps;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unique_ptr<SkAtlasTextTarget> SkAtlasTextTarget::Make(sk_sp<SkAtlasTextContext> context,
|
||||
int width, int height, void* handle) {
|
||||
return std::unique_ptr<SkAtlasTextTarget>(
|
||||
new SkInternalAtlasTextTarget(std::move(context), width, height, handle));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "GrContextPriv.h"
|
||||
#include "GrDrawingManager.h"
|
||||
|
||||
void SkInternalAtlasTextTarget::drawText(const void* text, size_t byteLength, SkScalar x,
|
||||
SkScalar y, uint32_t color, const SkAtlasTextFont& font) {
|
||||
SkPaint paint;
|
||||
paint.setAntiAlias(true);
|
||||
paint.setTypeface(font.refTypeface());
|
||||
paint.setTextSize(font.size());
|
||||
paint.setStyle(SkPaint::kFill_Style);
|
||||
|
||||
// TODO: Figure out what if anything to do with these:
|
||||
// Paint setTextEncoding? Font isEnableByteCodeHints()? Font isUseNonLinearMetrics()?
|
||||
|
||||
// The atlas text context does munging of the paint color. We store the client's color here
|
||||
// and the context will write it into the final vertices given to the client's renderer.
|
||||
fColor = color;
|
||||
|
||||
// The pixel geometry here is arbitrary. We don't draw LCD text.
|
||||
SkSurfaceProps props(SkSurfaceProps::kUseDistanceFieldFonts_Flag, kUnknown_SkPixelGeometry);
|
||||
auto* grContext = this->context()->internal().grContext();
|
||||
auto bounds = SkIRect::MakeWH(fWidth, fHeight);
|
||||
auto atlasTextContext = grContext->contextPriv().drawingManager()->getAtlasTextContext();
|
||||
atlasTextContext->drawText(grContext, this, GrNoClip(), paint, SkMatrix::I(), props,
|
||||
(const char*)text, byteLength, x, y, bounds);
|
||||
}
|
||||
|
||||
void SkInternalAtlasTextTarget::addDrawOp(const GrClip& clip, std::unique_ptr<GrAtlasTextOp> op) {
|
||||
SkASSERT(clip.quickContains(SkRect::MakeIWH(fWidth, fHeight)));
|
||||
// The SkAtlasTextRenderer currently only handles grayscale SDF glyphs.
|
||||
if (op->maskType() != GrAtlasTextOp::kGrayscaleDistanceField_MaskType) {
|
||||
return;
|
||||
}
|
||||
// TODO: batch ops here.
|
||||
op->visitProxies([](GrSurfaceProxy*) {});
|
||||
fOps.emplace_back(RecordedOp{std::move(op), fColor});
|
||||
}
|
||||
|
||||
void SkInternalAtlasTextTarget::flush() {
|
||||
for (int i = 0; i < fOps.count(); ++i) {
|
||||
fOps[i].fOp->executeForTextTarget(this, fOps[i].fColor);
|
||||
}
|
||||
this->context()->internal().flush();
|
||||
fOps.reset();
|
||||
}
|
||||
|
||||
void GrAtlasTextOp::executeForTextTarget(SkAtlasTextTarget* target, uint32_t color) {
|
||||
FlushInfo flushInfo;
|
||||
SkAutoGlyphCache glyphCache;
|
||||
auto& context = target->context()->internal();
|
||||
auto* atlasGlyphCache = context.grContext()->getAtlasGlyphCache();
|
||||
for (int i = 0; i < fGeoCount; ++i) {
|
||||
GrAtlasTextBlob::VertexRegenerator regenerator(
|
||||
fGeoData[i].fBlob, fGeoData[i].fRun, fGeoData[i].fSubRun, fGeoData[i].fViewMatrix,
|
||||
fGeoData[i].fX, fGeoData[i].fY, color, &context, atlasGlyphCache, &glyphCache);
|
||||
GrAtlasTextBlob::VertexRegenerator::Result result;
|
||||
do {
|
||||
result = regenerator.regenerate();
|
||||
context.recordDraw(result.fFirstVertex, result.fGlyphsRegenerated, target->handle());
|
||||
if (!result.fFinished) {
|
||||
// Make space in the atlas so we can continue generating vertices.
|
||||
context.flush();
|
||||
}
|
||||
} while (!result.fFinished);
|
||||
}
|
||||
}
|
117
src/atlastext/SkInternalAtlasTextContext.cpp
Normal file
117
src/atlastext/SkInternalAtlasTextContext.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkInternalAtlasTextContext.h"
|
||||
#include "GrContext.h"
|
||||
#include "SkAtlasTextContext.h"
|
||||
#include "SkAtlasTextRenderer.h"
|
||||
#include "text/GrAtlasGlyphCache.h"
|
||||
|
||||
SkAtlasTextRenderer* SkGetAtlasTextRendererFromInternalContext(
|
||||
class SkInternalAtlasTextContext& internal) {
|
||||
return internal.renderer();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unique_ptr<SkInternalAtlasTextContext> SkInternalAtlasTextContext::Make(
|
||||
sk_sp<SkAtlasTextRenderer> renderer) {
|
||||
return std::unique_ptr<SkInternalAtlasTextContext>(
|
||||
new SkInternalAtlasTextContext(std::move(renderer)));
|
||||
}
|
||||
|
||||
SkInternalAtlasTextContext::SkInternalAtlasTextContext(sk_sp<SkAtlasTextRenderer> renderer)
|
||||
: fRenderer(std::move(renderer)) {
|
||||
GrContextOptions options;
|
||||
options.fAllowMultipleGlyphCacheTextures = GrContextOptions::Enable::kNo;
|
||||
options.fMinDistanceFieldFontSize = 0.f;
|
||||
options.fGlyphsAsPathsFontSize = SK_ScalarInfinity;
|
||||
fGrContext = GrContext::MakeMock(nullptr, options);
|
||||
}
|
||||
|
||||
SkInternalAtlasTextContext::~SkInternalAtlasTextContext() {
|
||||
if (fDistanceFieldAtlas.fProxy) {
|
||||
SkASSERT(1 == fGrContext->getAtlasGlyphCache()->getAtlasPageCount(kA8_GrMaskFormat));
|
||||
fRenderer->deleteTexture(fDistanceFieldAtlas.fTextureHandle);
|
||||
}
|
||||
}
|
||||
|
||||
GrAtlasGlyphCache* SkInternalAtlasTextContext::atlasGlyphCache() {
|
||||
return fGrContext->getAtlasGlyphCache();
|
||||
}
|
||||
|
||||
GrTextBlobCache* SkInternalAtlasTextContext::textBlobCache() {
|
||||
return fGrContext->getTextBlobCache();
|
||||
}
|
||||
|
||||
GrDeferredUploadToken SkInternalAtlasTextContext::addInlineUpload(
|
||||
GrDeferredTextureUploadFn&& upload) {
|
||||
auto token = this->nextDrawToken();
|
||||
fInlineUploads.append(&fArena, InlineUpload{std::move(upload), token});
|
||||
return token;
|
||||
}
|
||||
|
||||
GrDeferredUploadToken SkInternalAtlasTextContext::addASAPUpload(
|
||||
GrDeferredTextureUploadFn&& upload) {
|
||||
fASAPUploads.append(&fArena, std::move(upload));
|
||||
return this->nextTokenToFlush();
|
||||
}
|
||||
|
||||
void SkInternalAtlasTextContext::recordDraw(const void* srcVertexData, int glyphCnt,
|
||||
void* targetHandle) {
|
||||
auto vertexDataSize = sizeof(SkAtlasTextRenderer::SDFVertex) * 4 * glyphCnt;
|
||||
auto vertexData = fArena.makeArrayDefault<char>(vertexDataSize);
|
||||
memcpy(vertexData, srcVertexData, vertexDataSize);
|
||||
for (int i = 0; i < 4 * glyphCnt; ++i) {
|
||||
auto* vertex = reinterpret_cast<SkAtlasTextRenderer::SDFVertex*>(vertexData) + i;
|
||||
// GrAtlasTextContext encodes a texture index into the lower bit of each texture coord.
|
||||
// This isn't expected by SkAtlasTextRenderer subclasses.
|
||||
vertex->fTextureCoord.fX /= 2;
|
||||
vertex->fTextureCoord.fY /= 2;
|
||||
}
|
||||
fDraws.append(&fArena, Draw{glyphCnt, this->issueDrawToken(), targetHandle, vertexData});
|
||||
}
|
||||
|
||||
void SkInternalAtlasTextContext::flush() {
|
||||
auto* atlasGlyphCache = fGrContext->getAtlasGlyphCache();
|
||||
if (!fDistanceFieldAtlas.fProxy) {
|
||||
SkASSERT(1 == atlasGlyphCache->getAtlasPageCount(kA8_GrMaskFormat));
|
||||
fDistanceFieldAtlas.fProxy = atlasGlyphCache->getProxies(kA8_GrMaskFormat)->get();
|
||||
fDistanceFieldAtlas.fTextureHandle =
|
||||
fRenderer->createTexture(SkAtlasTextRenderer::AtlasFormat::kA8,
|
||||
fDistanceFieldAtlas.fProxy->width(),
|
||||
fDistanceFieldAtlas.fProxy->height());
|
||||
}
|
||||
GrDeferredTextureUploadWritePixelsFn writePixelsFn =
|
||||
[this](GrTextureProxy* proxy, int left, int top, int width, int height,
|
||||
GrPixelConfig config, const void* data, size_t rowBytes) -> bool {
|
||||
SkASSERT(kAlpha_8_GrPixelConfig == config);
|
||||
SkASSERT(proxy == this->fDistanceFieldAtlas.fProxy);
|
||||
void* handle = fDistanceFieldAtlas.fTextureHandle;
|
||||
this->fRenderer->setTextureData(handle, data, left, top, width, height, rowBytes);
|
||||
return true;
|
||||
};
|
||||
for (const auto& upload : fASAPUploads) {
|
||||
upload(writePixelsFn);
|
||||
}
|
||||
auto draw = fDraws.begin();
|
||||
auto inlineUpload = fInlineUploads.begin();
|
||||
while (draw != fDraws.end()) {
|
||||
while (inlineUpload != fInlineUploads.end() && inlineUpload->fToken == draw->fToken) {
|
||||
inlineUpload->fUpload(writePixelsFn);
|
||||
++inlineUpload;
|
||||
}
|
||||
auto vertices = reinterpret_cast<const SkAtlasTextRenderer::SDFVertex*>(draw->fVertexData);
|
||||
fRenderer->drawSDFGlyphs(draw->fTargetHandle, fDistanceFieldAtlas.fTextureHandle, vertices,
|
||||
draw->fGlyphCnt);
|
||||
++draw;
|
||||
}
|
||||
fASAPUploads.reset();
|
||||
fInlineUploads.reset();
|
||||
fDraws.reset();
|
||||
fArena.reset();
|
||||
}
|
80
src/atlastext/SkInternalAtlasTextContext.h
Normal file
80
src/atlastext/SkInternalAtlasTextContext.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkInternalAtlasTextContext_DEFINED
|
||||
#define SkInternalAtlasTextContext_DEFINED
|
||||
|
||||
#include "GrDeferredUpload.h"
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkArenaAllocList.h"
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
class SkAtlasTextRenderer;
|
||||
class GrContext;
|
||||
class GrAtlasGlyphCache;
|
||||
class GrTextBlobCache;
|
||||
|
||||
/**
|
||||
* The implementation of SkAtlasTextContext. This exists to hide the details from the public class.
|
||||
* and to be able to use other private types.
|
||||
*/
|
||||
class SkInternalAtlasTextContext : public GrDeferredUploadTarget {
|
||||
public:
|
||||
static std::unique_ptr<SkInternalAtlasTextContext> Make(sk_sp<SkAtlasTextRenderer>);
|
||||
|
||||
~SkInternalAtlasTextContext() override;
|
||||
|
||||
SkAtlasTextRenderer* renderer() const { return fRenderer.get(); }
|
||||
|
||||
GrContext* grContext() const { return fGrContext.get(); }
|
||||
GrAtlasGlyphCache* atlasGlyphCache();
|
||||
GrTextBlobCache* textBlobCache();
|
||||
|
||||
GrDeferredUploadToken addInlineUpload(GrDeferredTextureUploadFn&&) override;
|
||||
|
||||
GrDeferredUploadToken addASAPUpload(GrDeferredTextureUploadFn&&) override;
|
||||
|
||||
void recordDraw(const void* vertexData, int glyphCnt, void* targetHandle);
|
||||
|
||||
void flush();
|
||||
|
||||
private:
|
||||
class DeferredUploader;
|
||||
SkInternalAtlasTextContext() = delete;
|
||||
SkInternalAtlasTextContext(const SkInternalAtlasTextContext&) = delete;
|
||||
SkInternalAtlasTextContext& operator=(const SkInternalAtlasTextContext&) = delete;
|
||||
|
||||
SkInternalAtlasTextContext(sk_sp<SkAtlasTextRenderer>);
|
||||
|
||||
sk_sp<SkAtlasTextRenderer> fRenderer;
|
||||
|
||||
struct AtlasTexture {
|
||||
void* fTextureHandle = nullptr;
|
||||
GrTextureProxy* fProxy = nullptr;
|
||||
};
|
||||
|
||||
struct Draw {
|
||||
int fGlyphCnt;
|
||||
GrDeferredUploadToken fToken;
|
||||
void* fTargetHandle;
|
||||
const void* fVertexData;
|
||||
};
|
||||
|
||||
struct InlineUpload {
|
||||
GrDeferredTextureUploadFn fUpload;
|
||||
GrDeferredUploadToken fToken;
|
||||
};
|
||||
|
||||
SkArenaAllocList<InlineUpload> fInlineUploads;
|
||||
SkArenaAllocList<Draw> fDraws;
|
||||
SkArenaAllocList<GrDeferredTextureUploadFn> fASAPUploads;
|
||||
SkArenaAlloc fArena{1024 * 40};
|
||||
sk_sp<GrContext> fGrContext;
|
||||
AtlasTexture fDistanceFieldAtlas;
|
||||
};
|
||||
|
||||
#endif
|
@ -5,8 +5,8 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkFixedAlloc_DEFINED
|
||||
#define SkFixedAlloc_DEFINED
|
||||
#ifndef SkArenaAlloc_DEFINED
|
||||
#define SkArenaAlloc_DEFINED
|
||||
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkTFitsIn.h"
|
||||
@ -240,4 +240,4 @@ private:
|
||||
using INHERITED = SkArenaAlloc;
|
||||
};
|
||||
|
||||
#endif//SkFixedAlloc_DEFINED
|
||||
#endif // SkArenaAlloc_DEFINED
|
||||
|
79
src/core/SkArenaAllocList.h
Normal file
79
src/core/SkArenaAllocList.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkArenaAllocList_DEFINED
|
||||
#define SkArenaAllocList_DEFINED
|
||||
|
||||
#include "SkArenaAlloc.h"
|
||||
|
||||
/**
|
||||
* A singly linked list of Ts stored in a SkArenaAlloc. The arena rather than the list owns
|
||||
* the elements. This supports forward iteration and range based for loops.
|
||||
*/
|
||||
template <typename T>
|
||||
class SkArenaAllocList {
|
||||
private:
|
||||
struct Node;
|
||||
|
||||
public:
|
||||
SkArenaAllocList() = default;
|
||||
|
||||
void reset() { fHead = fTail = nullptr; }
|
||||
|
||||
template <typename... Args>
|
||||
inline T& append(SkArenaAlloc* arena, Args... args);
|
||||
|
||||
class Iter {
|
||||
public:
|
||||
Iter() = default;
|
||||
inline Iter& operator++();
|
||||
T& operator*() const { return fCurr->fT; }
|
||||
T* operator->() const { return &fCurr->fT; }
|
||||
bool operator==(const Iter& that) const { return fCurr == that.fCurr; }
|
||||
bool operator!=(const Iter& that) const { return !(*this == that); }
|
||||
|
||||
private:
|
||||
friend class SkArenaAllocList;
|
||||
explicit Iter(Node* node) : fCurr(node) {}
|
||||
Node* fCurr = nullptr;
|
||||
};
|
||||
|
||||
Iter begin() { return Iter(fHead); }
|
||||
Iter end() { return Iter(); }
|
||||
Iter tail() { return Iter(fTail); }
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
template <typename... Args>
|
||||
Node(Args... args) : fT(std::forward<Args>(args)...) {}
|
||||
T fT;
|
||||
Node* fNext = nullptr;
|
||||
};
|
||||
Node* fHead = nullptr;
|
||||
Node* fTail = nullptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
template <typename... Args>
|
||||
T& SkArenaAllocList<T>::append(SkArenaAlloc* arena, Args... args) {
|
||||
SkASSERT(!fHead == !fTail);
|
||||
auto* n = arena->make<Node>(std::forward<Args>(args)...);
|
||||
if (!fTail) {
|
||||
fHead = fTail = n;
|
||||
} else {
|
||||
fTail = fTail->fNext = n;
|
||||
}
|
||||
return fTail->fT;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename SkArenaAllocList<T>::Iter& SkArenaAllocList<T>::Iter::operator++() {
|
||||
fCurr = fCurr->fNext;
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif
|
@ -12,25 +12,6 @@
|
||||
#include "GrResourceProvider.h"
|
||||
#include "GrTexture.h"
|
||||
|
||||
template <typename T>
|
||||
template <typename... Args>
|
||||
T& GrOpFlushState::List<T>::append(SkArenaAlloc* arena, Args... args) {
|
||||
SkASSERT(!fHead == !fTail);
|
||||
auto* n = arena->make<Node>(std::forward<Args>(args)...);
|
||||
if (!fTail) {
|
||||
fHead = fTail = n;
|
||||
} else {
|
||||
fTail = fTail->fNext = n;
|
||||
}
|
||||
return fTail->fT;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename GrOpFlushState::List<T>::Iter& GrOpFlushState::List<T>::Iter::operator++() {
|
||||
fCurr = fCurr->fNext;
|
||||
return *this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
GrOpFlushState::GrOpFlushState(GrGpu* gpu, GrResourceProvider* resourceProvider)
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "GrBufferAllocPool.h"
|
||||
#include "GrDeferredUpload.h"
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkArenaAllocList.h"
|
||||
#include "ops/GrMeshDrawOp.h"
|
||||
|
||||
class GrGpu;
|
||||
@ -110,53 +111,6 @@ private:
|
||||
uint32_t fOpID;
|
||||
};
|
||||
|
||||
/**
|
||||
* A singly linked list of Ts stored in a SkArenaAlloc. The arena rather than the list owns
|
||||
* the elements. This supports forward iteration and range based for loops.
|
||||
*/
|
||||
template <typename T>
|
||||
class List {
|
||||
private:
|
||||
struct Node;
|
||||
|
||||
public:
|
||||
List() = default;
|
||||
|
||||
void reset() { fHead = fTail = nullptr; }
|
||||
|
||||
template <typename... Args>
|
||||
T& append(SkArenaAlloc* arena, Args... args);
|
||||
|
||||
class Iter {
|
||||
public:
|
||||
Iter() = default;
|
||||
Iter& operator++();
|
||||
T& operator*() const { return fCurr->fT; }
|
||||
T* operator->() const { return &fCurr->fT; }
|
||||
bool operator==(const Iter& that) const { return fCurr == that.fCurr; }
|
||||
bool operator!=(const Iter& that) const { return !(*this == that); }
|
||||
|
||||
private:
|
||||
friend class List;
|
||||
explicit Iter(Node* node) : fCurr(node) {}
|
||||
Node* fCurr = nullptr;
|
||||
};
|
||||
|
||||
Iter begin() { return Iter(fHead); }
|
||||
Iter end() { return Iter(); }
|
||||
Iter tail() { return Iter(fTail); }
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
template <typename... Args>
|
||||
Node(Args... args) : fT(std::forward<Args>(args)...) {}
|
||||
T fT;
|
||||
Node* fNext = nullptr;
|
||||
};
|
||||
Node* fHead = nullptr;
|
||||
Node* fTail = nullptr;
|
||||
};
|
||||
|
||||
// Storage for ops' pipelines, draws, and inline uploads.
|
||||
SkArenaAlloc fArena{sizeof(GrPipeline) * 100};
|
||||
|
||||
@ -165,9 +119,9 @@ private:
|
||||
GrIndexBufferAllocPool fIndexPool;
|
||||
|
||||
// Data stored on behalf of the ops being flushed.
|
||||
List<GrDeferredTextureUploadFn> fAsapUploads;
|
||||
List<InlineUpload> fInlineUploads;
|
||||
List<Draw> fDraws;
|
||||
SkArenaAllocList<GrDeferredTextureUploadFn> fAsapUploads;
|
||||
SkArenaAllocList<InlineUpload> fInlineUploads;
|
||||
SkArenaAllocList<Draw> fDraws;
|
||||
// TODO: These should go in the arena. However, GrGpuCommandBuffer and other classes currently
|
||||
// accept contiguous arrays of meshes.
|
||||
SkSTArray<16, GrMesh> fMeshes;
|
||||
@ -185,9 +139,9 @@ private:
|
||||
GrGpuCommandBuffer* fCommandBuffer = nullptr;
|
||||
|
||||
// Variables that are used to track where we are in lists as ops are executed
|
||||
List<Draw>::Iter fCurrDraw;
|
||||
SkArenaAllocList<Draw>::Iter fCurrDraw;
|
||||
int fCurrMesh;
|
||||
List<InlineUpload>::Iter fCurrUpload;
|
||||
SkArenaAllocList<InlineUpload>::Iter fCurrUpload;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "text/GrAtlasTextContext.h"
|
||||
#include "text/GrDistanceFieldAdjustTable.h"
|
||||
|
||||
class SkAtlasTextTarget;
|
||||
|
||||
class GrAtlasTextOp final : public GrMeshDrawOp {
|
||||
public:
|
||||
DEFINE_OP_CLASS_ID
|
||||
@ -116,6 +118,20 @@ public:
|
||||
RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
|
||||
GrPixelConfigIsClamped dstIsClamped) override;
|
||||
|
||||
enum MaskType {
|
||||
kGrayscaleCoverageMask_MaskType,
|
||||
kLCDCoverageMask_MaskType,
|
||||
kColorBitmapMask_MaskType,
|
||||
kAliasedDistanceField_MaskType,
|
||||
kGrayscaleDistanceField_MaskType,
|
||||
kLCDDistanceField_MaskType,
|
||||
kLCDBGRDistanceField_MaskType,
|
||||
};
|
||||
|
||||
MaskType maskType() const { return fMaskType; }
|
||||
|
||||
void executeForTextTarget(SkAtlasTextTarget*, uint32_t color);
|
||||
|
||||
private:
|
||||
// The minimum number of Geometry we will try to allocate.
|
||||
static constexpr auto kMinGeometryAllocated = 12;
|
||||
@ -180,16 +196,6 @@ private:
|
||||
|
||||
sk_sp<GrGeometryProcessor> setupDfProcessor() const;
|
||||
|
||||
enum MaskType {
|
||||
kGrayscaleCoverageMask_MaskType,
|
||||
kLCDCoverageMask_MaskType,
|
||||
kColorBitmapMask_MaskType,
|
||||
kAliasedDistanceField_MaskType,
|
||||
kGrayscaleDistanceField_MaskType,
|
||||
kLCDDistanceField_MaskType,
|
||||
kLCDBGRDistanceField_MaskType,
|
||||
};
|
||||
|
||||
SkAutoSTMalloc<kMinGeometryAllocated, Geometry> fGeoData;
|
||||
int fGeoDataAllocSize;
|
||||
GrColor fColor;
|
||||
|
439
tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp
Normal file
439
tools/gpu/atlastext/GLTestAtlasTextRenderer.cpp
Normal file
@ -0,0 +1,439 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "GLTestAtlasTextRenderer.h"
|
||||
#include "../gl/GLTestContext.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "TestAtlasTextRenderer.h"
|
||||
#include "gl/GrGLDefines.h"
|
||||
|
||||
using sk_gpu_test::GLTestContext;
|
||||
|
||||
namespace {
|
||||
|
||||
class GLTestAtlasTextRenderer : public sk_gpu_test::TestAtlasTextRenderer {
|
||||
public:
|
||||
GLTestAtlasTextRenderer(std::unique_ptr<GLTestContext>);
|
||||
|
||||
void* createTexture(AtlasFormat, int width, int height) override;
|
||||
|
||||
void deleteTexture(void* textureHandle) override;
|
||||
|
||||
void setTextureData(void* textureHandle, const void* data, int x, int y, int width, int height,
|
||||
size_t rowBytes) override;
|
||||
|
||||
void drawSDFGlyphs(void* targetHandle, void* textureHandle, const SDFVertex vertices[],
|
||||
int quadCnt) override;
|
||||
|
||||
void* makeTargetHandle(int width, int height) override;
|
||||
|
||||
void targetDeleted(void* target) override;
|
||||
|
||||
SkBitmap readTargetHandle(void* target) override;
|
||||
|
||||
bool initialized() const { return 0 != fProgram; }
|
||||
|
||||
private:
|
||||
struct AtlasTexture {
|
||||
GrGLuint fID;
|
||||
AtlasFormat fFormat;
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
};
|
||||
|
||||
struct Target {
|
||||
GrGLuint fFBOID;
|
||||
GrGLuint fRBID;
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
};
|
||||
|
||||
std::unique_ptr<GLTestContext> fContext;
|
||||
GrGLuint fProgram = 0;
|
||||
GrGLint fDstScaleAndTranslateLocation = 0;
|
||||
GrGLint fAtlasInvSizeLocation = 0;
|
||||
GrGLint fSamplerLocation = 0;
|
||||
};
|
||||
|
||||
#define callgl(NAME, ...) fContext->gl()->fFunctions.f##NAME(__VA_ARGS__)
|
||||
#define checkgl() \
|
||||
do { \
|
||||
static constexpr auto line = __LINE__; \
|
||||
auto error = fContext->gl()->fFunctions.fGetError(); \
|
||||
if (error != GR_GL_NO_ERROR) { \
|
||||
SkDebugf("GL ERROR: 0x%x, line %d\n", error, line); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
GLTestAtlasTextRenderer::GLTestAtlasTextRenderer(std::unique_ptr<GLTestContext> context)
|
||||
: fContext(std::move(context)) {
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
auto vs = callgl(CreateShader, GR_GL_VERTEX_SHADER);
|
||||
static constexpr char kGLVersionString[] = "#version 430 compatibility";
|
||||
static constexpr char kGLESVersionString[] = "#version 300 es";
|
||||
GrGLint lengths[2];
|
||||
const GrGLchar* strings[2];
|
||||
switch (fContext->gl()->fStandard) {
|
||||
case kGL_GrGLStandard:
|
||||
strings[0] = kGLVersionString;
|
||||
lengths[0] = static_cast<GrGLint>(SK_ARRAY_COUNT(kGLVersionString)) - 1;
|
||||
break;
|
||||
case kGLES_GrGLStandard:
|
||||
strings[0] = kGLESVersionString;
|
||||
lengths[0] = static_cast<GrGLint>(SK_ARRAY_COUNT(kGLESVersionString)) - 1;
|
||||
break;
|
||||
default:
|
||||
strings[0] = nullptr;
|
||||
lengths[0] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
static constexpr const char kVS[] = R"(
|
||||
uniform vec4 uDstScaleAndTranslate;
|
||||
uniform vec2 uAtlasInvSize;
|
||||
|
||||
layout (location = 0) in vec2 inPosition;
|
||||
layout (location = 1) in vec4 inColor;
|
||||
layout (location = 2) in uvec2 inTextureCoords;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
out vec4 vColor;
|
||||
out vec2 vIntTexCoord;
|
||||
|
||||
void main() {
|
||||
vec2 intCoords;
|
||||
// floor(vec2) doesn't seem to work on some ES devices.
|
||||
intCoords.x = floor(float(inTextureCoords.x));
|
||||
intCoords.y = floor(float(inTextureCoords.y));
|
||||
vTexCoord = intCoords * uAtlasInvSize;
|
||||
vIntTexCoord = intCoords;
|
||||
vColor = inColor;
|
||||
gl_Position = vec4(inPosition.x * uDstScaleAndTranslate.x + uDstScaleAndTranslate.y,
|
||||
inPosition.y * uDstScaleAndTranslate.z + uDstScaleAndTranslate.w,
|
||||
0.0, 1.0);
|
||||
}
|
||||
)";
|
||||
strings[1] = kVS;
|
||||
lengths[1] = SK_ARRAY_COUNT(kVS) - 1;
|
||||
callgl(ShaderSource, vs, 2, strings, lengths);
|
||||
callgl(CompileShader, vs);
|
||||
GrGLint compileStatus;
|
||||
callgl(GetShaderiv, vs, GR_GL_COMPILE_STATUS, &compileStatus);
|
||||
if (compileStatus == GR_GL_FALSE) {
|
||||
GrGLint logLength;
|
||||
callgl(GetShaderiv, vs, GR_GL_INFO_LOG_LENGTH, &logLength);
|
||||
std::unique_ptr<GrGLchar[]> log(new GrGLchar[logLength + 1]);
|
||||
log[logLength] = '\0';
|
||||
callgl(GetShaderInfoLog, vs, logLength, &logLength, log.get());
|
||||
SkDebugf("Vertex Shader failed to compile\n%s", log.get());
|
||||
callgl(DeleteShader, vs);
|
||||
return;
|
||||
}
|
||||
|
||||
auto fs = callgl(CreateShader, GR_GL_FRAGMENT_SHADER);
|
||||
static constexpr const char kFS[] = R"(
|
||||
uniform sampler2D uSampler;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
in vec4 vColor;
|
||||
in vec2 vIntTexCoord;
|
||||
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
float sdfValue = texture(uSampler, vTexCoord).r;
|
||||
float distance = 7.96875 * (sdfValue - 0.50196078431000002);
|
||||
vec2 dist_grad = vec2(dFdx(distance), dFdy(distance));
|
||||
vec2 Jdx = dFdx(vIntTexCoord);
|
||||
vec2 Jdy = dFdy(vIntTexCoord);
|
||||
float dg_len2 = dot(dist_grad, dist_grad);
|
||||
if (dg_len2 < 0.0001) {
|
||||
dist_grad = vec2(0.7071, 0.7071);
|
||||
} else {
|
||||
dist_grad = dist_grad * inversesqrt(dg_len2);
|
||||
}
|
||||
vec2 grad = vec2(dist_grad.x * Jdx.x + dist_grad.y * Jdy.x,
|
||||
dist_grad.x * Jdx.y + dist_grad.y * Jdy.y);
|
||||
float afwidth = abs(0.65000000000000002 * length(grad));
|
||||
float value = smoothstep(-afwidth, afwidth, distance);
|
||||
outColor = value * vec4(vColor.rgb * vColor.a, vColor.a);
|
||||
}
|
||||
)";
|
||||
strings[1] = kFS;
|
||||
lengths[1] = SK_ARRAY_COUNT(kFS) - 1;
|
||||
callgl(ShaderSource, fs, 2, strings, lengths);
|
||||
callgl(CompileShader, fs);
|
||||
callgl(GetShaderiv, fs, GR_GL_COMPILE_STATUS, &compileStatus);
|
||||
if (compileStatus == GR_GL_FALSE) {
|
||||
GrGLint logLength;
|
||||
callgl(GetShaderiv, fs, GR_GL_INFO_LOG_LENGTH, &logLength);
|
||||
std::unique_ptr<GrGLchar[]> log(new GrGLchar[logLength + 1]);
|
||||
log[logLength] = '\0';
|
||||
callgl(GetShaderInfoLog, fs, logLength, &logLength, log.get());
|
||||
SkDebugf("Fragment Shader failed to compile\n%s", log.get());
|
||||
callgl(DeleteShader, vs);
|
||||
callgl(DeleteShader, fs);
|
||||
return;
|
||||
}
|
||||
|
||||
fProgram = callgl(CreateProgram);
|
||||
if (!fProgram) {
|
||||
callgl(DeleteShader, vs);
|
||||
callgl(DeleteShader, fs);
|
||||
return;
|
||||
}
|
||||
|
||||
callgl(AttachShader, fProgram, vs);
|
||||
callgl(AttachShader, fProgram, fs);
|
||||
callgl(LinkProgram, fProgram);
|
||||
GrGLint linkStatus;
|
||||
callgl(GetProgramiv, fProgram, GR_GL_LINK_STATUS, &linkStatus);
|
||||
if (linkStatus == GR_GL_FALSE) {
|
||||
GrGLint logLength = 0;
|
||||
callgl(GetProgramiv, vs, GR_GL_INFO_LOG_LENGTH, &logLength);
|
||||
std::unique_ptr<GrGLchar[]> log(new GrGLchar[logLength + 1]);
|
||||
log[logLength] = '\0';
|
||||
callgl(GetProgramInfoLog, vs, logLength, &logLength, log.get());
|
||||
SkDebugf("Program failed to link\n%s", log.get());
|
||||
callgl(DeleteShader, vs);
|
||||
callgl(DeleteShader, fs);
|
||||
callgl(DeleteProgram, fProgram);
|
||||
fProgram = 0;
|
||||
return;
|
||||
}
|
||||
fDstScaleAndTranslateLocation = callgl(GetUniformLocation, fProgram, "uDstScaleAndTranslate");
|
||||
fAtlasInvSizeLocation = callgl(GetUniformLocation, fProgram, "uAtlasInvSize");
|
||||
fSamplerLocation = callgl(GetUniformLocation, fProgram, "uSampler");
|
||||
if (fDstScaleAndTranslateLocation < 0 || fAtlasInvSizeLocation < 0 || fSamplerLocation < 0) {
|
||||
callgl(DeleteShader, vs);
|
||||
callgl(DeleteShader, fs);
|
||||
callgl(DeleteProgram, fProgram);
|
||||
fProgram = 0;
|
||||
}
|
||||
|
||||
checkgl();
|
||||
}
|
||||
|
||||
inline bool atlas_format_to_gl_types(SkAtlasTextRenderer::AtlasFormat format,
|
||||
GrGLenum* internalFormat, GrGLenum* externalFormat,
|
||||
GrGLenum* type) {
|
||||
switch (format) {
|
||||
case SkAtlasTextRenderer::AtlasFormat::kA8:
|
||||
*internalFormat = GR_GL_R8;
|
||||
*externalFormat = GR_GL_RED;
|
||||
*type = GR_GL_UNSIGNED_BYTE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline int atlas_format_bytes_per_pixel(SkAtlasTextRenderer::AtlasFormat format) {
|
||||
switch (format) {
|
||||
case SkAtlasTextRenderer::AtlasFormat::kA8:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* GLTestAtlasTextRenderer::createTexture(AtlasFormat format, int width, int height) {
|
||||
GrGLenum internalFormat;
|
||||
GrGLenum externalFormat;
|
||||
GrGLenum type;
|
||||
if (!atlas_format_to_gl_types(format, &internalFormat, &externalFormat, &type)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
|
||||
GrGLuint id;
|
||||
callgl(GenTextures, 1, &id);
|
||||
if (!id) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
callgl(BindTexture, GR_GL_TEXTURE_2D, id);
|
||||
callgl(TexImage2D, GR_GL_TEXTURE_2D, 0, internalFormat, width, height, 0, externalFormat, type,
|
||||
nullptr);
|
||||
checkgl();
|
||||
|
||||
AtlasTexture* atlas = new AtlasTexture;
|
||||
atlas->fID = id;
|
||||
atlas->fFormat = format;
|
||||
atlas->fWidth = width;
|
||||
atlas->fHeight = height;
|
||||
return atlas;
|
||||
}
|
||||
|
||||
void GLTestAtlasTextRenderer::deleteTexture(void* textureHandle) {
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
|
||||
auto* atlasTexture = reinterpret_cast<const AtlasTexture*>(textureHandle);
|
||||
|
||||
callgl(DeleteTextures, 1, &atlasTexture->fID);
|
||||
checkgl();
|
||||
|
||||
delete atlasTexture;
|
||||
}
|
||||
|
||||
void GLTestAtlasTextRenderer::setTextureData(void* textureHandle, const void* data, int x, int y,
|
||||
int width, int height, size_t rowBytes) {
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
|
||||
auto atlasTexture = reinterpret_cast<const AtlasTexture*>(textureHandle);
|
||||
|
||||
GrGLenum internalFormat;
|
||||
GrGLenum externalFormat;
|
||||
GrGLenum type;
|
||||
if (!atlas_format_to_gl_types(atlasTexture->fFormat, &internalFormat, &externalFormat, &type)) {
|
||||
return;
|
||||
}
|
||||
int bpp = atlas_format_bytes_per_pixel(atlasTexture->fFormat);
|
||||
GrGLint rowLength = static_cast<GrGLint>(rowBytes / bpp);
|
||||
if (static_cast<size_t>(rowLength * bpp) != rowBytes) {
|
||||
return;
|
||||
}
|
||||
callgl(PixelStorei, GR_GL_UNPACK_ALIGNMENT, 1);
|
||||
callgl(PixelStorei, GR_GL_UNPACK_ROW_LENGTH, rowLength);
|
||||
callgl(BindTexture, GR_GL_TEXTURE_2D, atlasTexture->fID);
|
||||
callgl(TexSubImage2D, GR_GL_TEXTURE_2D, 0, x, y, width, height, externalFormat, type, data);
|
||||
checkgl();
|
||||
}
|
||||
|
||||
void GLTestAtlasTextRenderer::drawSDFGlyphs(void* targetHandle, void* textureHandle,
|
||||
const SDFVertex vertices[], int quadCnt) {
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
|
||||
auto target = reinterpret_cast<const Target*>(targetHandle);
|
||||
auto atlas = reinterpret_cast<const AtlasTexture*>(textureHandle);
|
||||
|
||||
callgl(UseProgram, fProgram);
|
||||
|
||||
callgl(ActiveTexture, GR_GL_TEXTURE0);
|
||||
callgl(BindTexture, GR_GL_TEXTURE_2D, atlas->fID);
|
||||
callgl(TexParameteri, GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER, GR_GL_LINEAR);
|
||||
callgl(TexParameteri, GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER, GR_GL_LINEAR);
|
||||
|
||||
float uniformScaleAndTranslate[4] = {2.f / target->fWidth, -1.f, 2.f / target->fHeight, -1.f};
|
||||
callgl(Uniform4fv, fDstScaleAndTranslateLocation, 1, uniformScaleAndTranslate);
|
||||
callgl(Uniform2f, fAtlasInvSizeLocation, 1.f / atlas->fWidth, 1.f / atlas->fHeight);
|
||||
callgl(Uniform1i, fSamplerLocation, 0);
|
||||
|
||||
callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, target->fFBOID);
|
||||
callgl(Viewport, 0, 0, target->fWidth, target->fHeight);
|
||||
|
||||
callgl(Enable, GR_GL_BLEND);
|
||||
callgl(BlendFunc, GR_GL_ONE, GR_GL_ONE_MINUS_SRC_ALPHA);
|
||||
callgl(Disable, GR_GL_DEPTH_TEST);
|
||||
|
||||
callgl(BindVertexArray, 0);
|
||||
callgl(BindBuffer, GR_GL_ARRAY_BUFFER, 0);
|
||||
callgl(BindBuffer, GR_GL_ELEMENT_ARRAY_BUFFER, 0);
|
||||
callgl(VertexAttribPointer, 0, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(SDFVertex), vertices);
|
||||
size_t colorOffset = 2 * sizeof(float);
|
||||
callgl(VertexAttribPointer, 1, 4, GR_GL_UNSIGNED_BYTE, GR_GL_TRUE, sizeof(SDFVertex),
|
||||
reinterpret_cast<const char*>(vertices) + colorOffset);
|
||||
size_t texOffset = colorOffset + sizeof(uint32_t);
|
||||
callgl(VertexAttribIPointer, 2, 2, GR_GL_UNSIGNED_SHORT, sizeof(SDFVertex),
|
||||
reinterpret_cast<const char*>(vertices) + texOffset);
|
||||
callgl(EnableVertexAttribArray, 0);
|
||||
callgl(EnableVertexAttribArray, 1);
|
||||
callgl(EnableVertexAttribArray, 2);
|
||||
|
||||
std::unique_ptr<uint16_t[]> indices(new uint16_t[quadCnt * 6]);
|
||||
for (int q = 0; q < quadCnt; ++q) {
|
||||
indices[q * 6 + 0] = 0 + 4 * q;
|
||||
indices[q * 6 + 1] = 1 + 4 * q;
|
||||
indices[q * 6 + 2] = 2 + 4 * q;
|
||||
indices[q * 6 + 3] = 2 + 4 * q;
|
||||
indices[q * 6 + 4] = 1 + 4 * q;
|
||||
indices[q * 6 + 5] = 3 + 4 * q;
|
||||
}
|
||||
callgl(DrawElements, GR_GL_TRIANGLES, 6 * quadCnt, GR_GL_UNSIGNED_SHORT, indices.get());
|
||||
checkgl();
|
||||
}
|
||||
|
||||
void* GLTestAtlasTextRenderer::makeTargetHandle(int width, int height) {
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
|
||||
GrGLuint fbo;
|
||||
callgl(GenFramebuffers, 1, &fbo);
|
||||
if (!fbo) {
|
||||
return nullptr;
|
||||
}
|
||||
GrGLuint rb;
|
||||
callgl(GenRenderbuffers, 1, &rb);
|
||||
if (!rb) {
|
||||
callgl(DeleteFramebuffers, 1, &fbo);
|
||||
return nullptr;
|
||||
}
|
||||
callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, fbo);
|
||||
callgl(BindRenderbuffer, GR_GL_RENDERBUFFER, rb);
|
||||
callgl(RenderbufferStorage, GR_GL_RENDERBUFFER, GR_GL_RGBA8, width, height);
|
||||
callgl(FramebufferRenderbuffer, GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0, GR_GL_RENDERBUFFER,
|
||||
rb);
|
||||
GrGLenum status = callgl(CheckFramebufferStatus, GR_GL_FRAMEBUFFER);
|
||||
if (GR_GL_FRAMEBUFFER_COMPLETE != status) {
|
||||
callgl(DeleteFramebuffers, 1, &fbo);
|
||||
callgl(DeleteRenderbuffers, 1, &rb);
|
||||
return nullptr;
|
||||
}
|
||||
callgl(Disable, GR_GL_SCISSOR_TEST);
|
||||
callgl(ClearColor, 0.5, 0.5, 0.5, 1.0);
|
||||
callgl(Clear, GR_GL_COLOR_BUFFER_BIT);
|
||||
checkgl();
|
||||
Target* target = new Target;
|
||||
target->fFBOID = fbo;
|
||||
target->fRBID = rb;
|
||||
target->fWidth = width;
|
||||
target->fHeight = height;
|
||||
return target;
|
||||
}
|
||||
|
||||
void GLTestAtlasTextRenderer::targetDeleted(void* target) {
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
|
||||
Target* t = reinterpret_cast<Target*>(target);
|
||||
callgl(DeleteFramebuffers, 1, &t->fFBOID);
|
||||
callgl(DeleteRenderbuffers, 1, &t->fRBID);
|
||||
delete t;
|
||||
}
|
||||
|
||||
SkBitmap GLTestAtlasTextRenderer::readTargetHandle(void* target) {
|
||||
auto restore = fContext->makeCurrentAndAutoRestore();
|
||||
|
||||
Target* t = reinterpret_cast<Target*>(target);
|
||||
|
||||
auto info =
|
||||
SkImageInfo::Make(t->fWidth, t->fHeight, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
|
||||
SkBitmap bmp;
|
||||
bmp.setInfo(info, sizeof(uint32_t) * t->fWidth);
|
||||
bmp.allocPixels();
|
||||
|
||||
callgl(BindFramebuffer, GR_GL_FRAMEBUFFER, t->fFBOID);
|
||||
callgl(ReadPixels, 0, 0, t->fWidth, t->fHeight, GR_GL_RGBA, GR_GL_UNSIGNED_BYTE,
|
||||
bmp.getPixels());
|
||||
checkgl();
|
||||
return bmp;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace sk_gpu_test {
|
||||
|
||||
sk_sp<TestAtlasTextRenderer> MakeGLTestAtlasTextRenderer() {
|
||||
std::unique_ptr<GLTestContext> context(CreatePlatformGLTestContext(kGL_GrGLStandard));
|
||||
if (!context) {
|
||||
context.reset(CreatePlatformGLTestContext(kGLES_GrGLStandard));
|
||||
}
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
auto restorer = context->makeCurrentAndAutoRestore();
|
||||
auto renderer = sk_make_sp<GLTestAtlasTextRenderer>(std::move(context));
|
||||
return renderer->initialized() ? std::move(renderer) : nullptr;
|
||||
}
|
||||
|
||||
} // namespace sk_gpu_test
|
25
tools/gpu/atlastext/GLTestAtlasTextRenderer.h
Normal file
25
tools/gpu/atlastext/GLTestAtlasTextRenderer.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GLTestAtlasTextRenderer_DEFINED
|
||||
#define GLTestAtlasTextRenderer_DEFINED
|
||||
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
namespace sk_gpu_test {
|
||||
|
||||
class TestAtlasTextRenderer;
|
||||
|
||||
/**
|
||||
* Creates a TestAtlasTextRenderer that uses its own OpenGL context to implement
|
||||
* SkAtlasTextRenderer.
|
||||
*/
|
||||
sk_sp<TestAtlasTextRenderer> MakeGLTestAtlasTextRenderer();
|
||||
|
||||
} // namespace sk_gpu_test
|
||||
|
||||
#endif
|
34
tools/gpu/atlastext/TestAtlasTextRenderer.h
Normal file
34
tools/gpu/atlastext/TestAtlasTextRenderer.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef TestAtlasTextRenderer_DEFINED
|
||||
#define TestAtlasTextRenderer_DEFINED
|
||||
|
||||
#include "SkAtlasTextRenderer.h"
|
||||
#include "SkBitmap.h"
|
||||
|
||||
namespace sk_gpu_test {
|
||||
|
||||
class TestContext;
|
||||
|
||||
/**
|
||||
* Base class for implementations of SkAtlasTextRenderer in order to test the SkAtlasText APIs.
|
||||
* Adds a helper for creating SkAtlasTextTargets and to read back the contents of a target as a
|
||||
* bitmap.
|
||||
*/
|
||||
class TestAtlasTextRenderer : public SkAtlasTextRenderer {
|
||||
public:
|
||||
/** Returns a handle that can be used to construct a SkAtlasTextTarget instance. */
|
||||
virtual void* makeTargetHandle(int width, int height) = 0;
|
||||
|
||||
/** Makes a SkBitmap of the target handle's contents. */
|
||||
virtual SkBitmap readTargetHandle(void* targetHandle) = 0;
|
||||
};
|
||||
|
||||
} // namespace sk_gpu_test
|
||||
|
||||
#endif
|
@ -107,6 +107,11 @@ GLXGLTestContext::GLXGLTestContext(GrGLStandard forcedGpuAPI, GLXGLTestContext*
|
||||
, fDisplay(nullptr)
|
||||
, fPixmap(0)
|
||||
, fGlxPixmap(0) {
|
||||
// We cross our fingers that this is the first X call in the program and that if the application
|
||||
// is actually threaded that this succeeds.
|
||||
static SkOnce gOnce;
|
||||
gOnce([] { XInitThreads(); });
|
||||
|
||||
fDisplay = get_display();
|
||||
|
||||
GLXContext glxShareContext = shareContext ? shareContext->fContext : nullptr;
|
||||
|
Loading…
Reference in New Issue
Block a user