diff options
-rw-r--r-- | gm/imagemagnifier.cpp | 63 | ||||
-rw-r--r-- | gyp/effects.gyp | 2 | ||||
-rw-r--r-- | gyp/gmslides.gypi | 1 | ||||
-rw-r--r-- | include/effects/SkMagnifierImageFilter.h | 37 | ||||
-rw-r--r-- | include/gpu/GrContext.h | 17 | ||||
-rw-r--r-- | src/effects/SkMagnifierImageFilter.cpp | 331 | ||||
-rw-r--r-- | src/gpu/SkGpuDevice.cpp | 1 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_chromium.cpp | 2 | ||||
-rw-r--r-- | src/ports/SkGlobalInitialization_default.cpp | 2 | ||||
-rw-r--r-- | tests/GLProgramsTest.cpp | 2 |
10 files changed, 457 insertions, 1 deletions
diff --git a/gm/imagemagnifier.cpp b/gm/imagemagnifier.cpp new file mode 100644 index 0000000000..f9aff281fe --- /dev/null +++ b/gm/imagemagnifier.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2012 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" +#include "SkMagnifierImageFilter.h" + +#define WIDTH 500 +#define HEIGHT 500 + +namespace skiagm { + +class ImageMagnifierGM : public GM { +public: + ImageMagnifierGM() { + this->setBGColor(0xFF000000); + } + +protected: + virtual SkString onShortName() { + return SkString("imagemagnifier"); + } + + virtual SkISize onISize() { + return make_isize(WIDTH, HEIGHT); + } + + virtual void onDraw(SkCanvas* canvas) { + SkPaint paint; + paint.setImageFilter( + new SkMagnifierImageFilter( + SkRect::MakeXYWH(SkIntToScalar(125), SkIntToScalar(125), + SkIntToScalar(WIDTH / 2), + SkIntToScalar(HEIGHT / 2)), + 100))->unref(); + canvas->saveLayer(NULL, &paint); + paint.setAntiAlias(true); + const char* str = "The quick brown fox jumped over the lazy dog."; + srand(1234); + for (int i = 0; i < 25; ++i) { + int x = rand() % WIDTH; + int y = rand() % HEIGHT; + paint.setColor(rand() % 0x1000000 | 0xFF000000); + paint.setTextSize(SkIntToScalar(rand() % 300)); + canvas->drawText(str, strlen(str), SkIntToScalar(x), + SkIntToScalar(y), paint); + } + canvas->restore(); + } + +private: + typedef GM INHERITED; +}; + +////////////////////////////////////////////////////////////////////////////// + +static GM* MyFactory(void*) { return new ImageMagnifierGM; } +static GMRegistry reg(MyFactory); + +} diff --git a/gyp/effects.gyp b/gyp/effects.gyp index ab695564f4..12362617e2 100644 --- a/gyp/effects.gyp +++ b/gyp/effects.gyp @@ -35,6 +35,7 @@ '../include/effects/SkTableColorFilter.h', '../include/effects/SkTableMaskFilter.h', '../include/effects/SkTransparentShader.h', + '../include/effects/SkMagnifierImageFilter.h', '../src/effects/Sk1DPathEffect.cpp', '../src/effects/Sk2DPathEffect.cpp', @@ -68,6 +69,7 @@ '../src/effects/SkTableMaskFilter.cpp', '../src/effects/SkTestImageFilters.cpp', '../src/effects/SkTransparentShader.cpp', + '../src/effects/SkMagnifierImageFilter.cpp', '../src/effects/gradients/SkBitmapCache.cpp', '../src/effects/gradients/SkBitmapCache.h', diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi index 27d4cb5fbf..fda02b6160 100644 --- a/gyp/gmslides.gypi +++ b/gyp/gmslides.gypi @@ -36,6 +36,7 @@ '../gm/hairmodes.cpp', '../gm/hittestpath.cpp', '../gm/imageblur.cpp', + '../gm/imagemagnifier.cpp', '../gm/lighting.cpp', '../gm/imagefiltersbase.cpp', '../gm/lcdtext.cpp', diff --git a/include/effects/SkMagnifierImageFilter.h b/include/effects/SkMagnifierImageFilter.h new file mode 100644 index 0000000000..144fdb42ca --- /dev/null +++ b/include/effects/SkMagnifierImageFilter.h @@ -0,0 +1,37 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + + +#ifndef SkMagnifierImageFilter_DEFINED +#define SkMagnifierImageFilter_DEFINED + +#include "SkRect.h" +#include "SkImageFilter.h" + +class SK_API SkMagnifierImageFilter : public SkImageFilter { +public: + SkMagnifierImageFilter(SkRect srcRect, SkScalar inset); + + virtual bool asNewCustomStage(GrCustomStage** stage, + GrTexture* texture) const SK_OVERRIDE; + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMagnifierImageFilter) + +protected: + explicit SkMagnifierImageFilter(SkFlattenableReadBuffer& buffer); + virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE; + + virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&, + SkBitmap* result, SkIPoint* offset) SK_OVERRIDE; + +private: + SkRect fSrcRect; + SkScalar fInset; + typedef SkImageFilter INHERITED; +}; + +#endif diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h index 6252cc29f8..52dafd0597 100644 --- a/include/gpu/GrContext.h +++ b/include/gpu/GrContext.h @@ -605,6 +605,23 @@ public: float sigmaX, float sigmaY); /** + * Zooms a subset of the texture to a larger size with a nice edge. + * The inner rectangle is a simple scaling of the texture by a factor of + * |zoom|. The outer |inset| pixels transition from the background texture + * to the zoomed coordinate system at a rate of + * (distance_to_edge / inset) ^2, producing a rounded lens effect. + * @param srcTexture The source texture to be zoomed. + * @param dstRect The destination rectangle. + * @param srcRect The source rectangle. Must be smaller than + * dstRect + * @param inset Number of pixels to blend along the edges. + * @return the zoomed texture, which is dstTexture. + */ + GrTexture* zoom(GrTexture* srcTexture, + const SkRect& dstRect, const SkRect& srcRect, float inset); + + + /** * This enum is used with the function below, applyMorphology. */ enum MorphologyType { diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp new file mode 100644 index 0000000000..dbd9135891 --- /dev/null +++ b/src/effects/SkMagnifierImageFilter.cpp @@ -0,0 +1,331 @@ +/* + * Copyright 2012 The Android Open Source Project + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitmap.h" +#include "SkMagnifierImageFilter.h" +#include "SkColorPriv.h" +#include "SkFlattenableBuffers.h" +#include "gl/GrGLTexture.h" + +#include <algorithm> + +//////////////////////////////////////////////////////////////////////////////// +#include "effects/GrSingleTextureEffect.h" +#include "gl/GrGLProgramStage.h" +#include "gl/GrGLSL.h" +#include "gl/GrGLTexture.h" +#include "GrProgramStageFactory.h" + +class GrGLMagnifierEffect; + +class GrMagnifierEffect : public GrSingleTextureEffect { + +public: + GrMagnifierEffect(GrTexture* texture, + float xOffset, + float yOffset, + float xZoom, + float yZoom, + float xInset, + float yInset) + : GrSingleTextureEffect(texture) + , fXOffset(xOffset) + , fYOffset(yOffset) + , fXZoom(xZoom) + , fYZoom(yZoom) + , fXInset(xInset) + , fYInset(yInset) {} + + virtual ~GrMagnifierEffect() {}; + + static const char* Name() { return "Magnifier"; } + + virtual const GrProgramStageFactory& getFactory() const SK_OVERRIDE; + virtual bool isEqual(const GrCustomStage&) const SK_OVERRIDE; + + float x_offset() const { return fXOffset; } + float y_offset() const { return fYOffset; } + float x_zoom() const { return fXZoom; } + float y_zoom() const { return fYZoom; } + float x_inset() const { return fXInset; } + float y_inset() const { return fYInset; } + + typedef GrGLMagnifierEffect GLProgramStage; + +private: + GR_DECLARE_CUSTOM_STAGE_TEST; + + float fXOffset; + float fYOffset; + float fXZoom; + float fYZoom; + float fXInset; + float fYInset; + + typedef GrSingleTextureEffect INHERITED; +}; + +// For brevity +typedef GrGLUniformManager::UniformHandle UniformHandle; + +class GrGLMagnifierEffect : public GrGLProgramStage { +public: + GrGLMagnifierEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage); + + virtual void setupVariables(GrGLShaderBuilder* state) SK_OVERRIDE; + virtual void emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) SK_OVERRIDE; + virtual void emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) SK_OVERRIDE; + + virtual void setData(const GrGLUniformManager& uman, + const GrCustomStage& data, + const GrRenderTarget*, + int stageNum) SK_OVERRIDE; + + static inline StageKey GenKey(const GrCustomStage&, const GrGLCaps&); + +private: + + UniformHandle fOffsetVar; + UniformHandle fZoomVar; + UniformHandle fInsetVar; + + typedef GrGLProgramStage INHERITED; +}; + +GrGLMagnifierEffect::GrGLMagnifierEffect(const GrProgramStageFactory& factory, + const GrCustomStage& stage) + : GrGLProgramStage(factory) + , fOffsetVar(GrGLUniformManager::kInvalidUniformHandle) + , fZoomVar(GrGLUniformManager::kInvalidUniformHandle) + , fInsetVar(GrGLUniformManager::kInvalidUniformHandle) { +} + +void GrGLMagnifierEffect::setupVariables(GrGLShaderBuilder* state) { + fOffsetVar = state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType | + GrGLShaderBuilder::kVertex_ShaderType, + kVec2f_GrSLType, "uOffset"); + fZoomVar = state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType | + GrGLShaderBuilder::kVertex_ShaderType, + kVec2f_GrSLType, "uZoom"); + fInsetVar = state->addUniform( + GrGLShaderBuilder::kFragment_ShaderType | + GrGLShaderBuilder::kVertex_ShaderType, + kVec2f_GrSLType, "uInset"); +} + +void GrGLMagnifierEffect::emitVS(GrGLShaderBuilder* state, + const char* vertexCoords) { +} + +void GrGLMagnifierEffect::emitFS(GrGLShaderBuilder* state, + const char* outputColor, + const char* inputColor, + const char* samplerName) { + SkString* code = &state->fFSCode; + + code->appendf("\t\tvec2 coord = %s;\n", state->fSampleCoords.c_str()); + code->appendf("\t\tvec2 zoom_coord = %s + %s / %s;\n", + state->getUniformCStr(fOffsetVar), + state->fSampleCoords.c_str(), + state->getUniformCStr(fZoomVar)); + + code->appendf("\t\tvec2 delta = min(coord, vec2(1.0, 1.0) - coord);\n"); + + code->appendf( + "\t\tdelta = delta / %s;\n", state->getUniformCStr(fInsetVar)); + + code->appendf("\t\tfloat weight = 0.0;\n"); + code->appendf("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n"); + code->appendf("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n"); + code->appendf("\t\t\tfloat dist = length(delta);\n"); + code->appendf("\t\t\tdist = max(2.0 - dist, 0.0);\n"); + code->appendf("\t\t\tweight = min(dist * dist, 1.0);\n"); + code->appendf("\t\t} else {\n"); + code->appendf("\t\t\tvec2 delta_squared = delta * delta;\n"); + code->appendf( + "\t\t\tweight = min(min(delta_squared.s, delta_squared.y), 1.0);\n"); + code->appendf("\t\t}\n"); + + code->appendf("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n"); + code->appendf("\t\tvec4 output_color = "); + state->emitTextureLookup(samplerName, "mix_coord"); + code->appendf(";\n"); + + code->appendf("\t\t%s = output_color;", outputColor); +} + +void GrGLMagnifierEffect::setData(const GrGLUniformManager& uman, + const GrCustomStage& data, + const GrRenderTarget*, + int stageNum) { + const GrMagnifierEffect& zoom = + static_cast<const GrMagnifierEffect&>(data); + + uman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset()); + uman.set2f(fZoomVar, zoom.x_zoom(), zoom.y_zoom()); + uman.set2f(fInsetVar, zoom.x_inset(), zoom.y_inset()); +} + +GrGLProgramStage::StageKey GrGLMagnifierEffect::GenKey(const GrCustomStage& s, + const GrGLCaps& caps) { + return 0; +} + +///////////////////////////////////////////////////////////////////// + +GR_DEFINE_CUSTOM_STAGE_TEST(GrMagnifierEffect); + +GrCustomStage* GrMagnifierEffect::TestCreate(SkRandom* random, + GrContext* context, + GrTexture** textures) { + const int kMaxWidth = 200; + const int kMaxHeight = 200; + const int kMaxInset = 20; + SkScalar width = random->nextULessThan(kMaxWidth); + SkScalar height = random->nextULessThan(kMaxHeight); + SkScalar x = random->nextULessThan(kMaxWidth - width); + SkScalar y = random->nextULessThan(kMaxHeight - height); + SkScalar inset = random->nextULessThan(kMaxInset); + + SkAutoTUnref<SkImageFilter> filter( + new SkMagnifierImageFilter( + SkRect::MakeXYWH(x, y, width, height), + inset)); + GrSamplerState sampler; + GrCustomStage* stage; + filter->asNewCustomStage(&stage, textures[0]); + GrAssert(NULL != stage); + return stage; +} + +/////////////////////////////////////////////////////////////////////////////// + +const GrProgramStageFactory& GrMagnifierEffect::getFactory() const { + return GrTProgramStageFactory<GrMagnifierEffect>::getInstance(); +} + +bool GrMagnifierEffect::isEqual(const GrCustomStage& sBase) const { + const GrMagnifierEffect& s = + static_cast<const GrMagnifierEffect&>(sBase); + return (this->fXOffset == s.fXOffset && + this->fYOffset == s.fYOffset && + this->fXZoom == s.fXZoom && + this->fYZoom == s.fYZoom && + this->fXInset == s.fXInset && + this->fYInset == s.fYInset); +} + +//////////////////////////////////////////////////////////////////////////////// +SkMagnifierImageFilter::SkMagnifierImageFilter(SkFlattenableReadBuffer& buffer) + : INHERITED(buffer) { + float x = buffer.readScalar(); + float y = buffer.readScalar(); + float width = buffer.readScalar(); + float height = buffer.readScalar(); + fSrcRect = SkRect::MakeXYWH(x, y, width, height); + fInset = buffer.readScalar(); +} + +SkMagnifierImageFilter::SkMagnifierImageFilter(SkRect srcRect, SkScalar inset) + : fSrcRect(srcRect), fInset(inset) { + SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0); +} + +bool SkMagnifierImageFilter::asNewCustomStage(GrCustomStage** stage, + GrTexture* texture) const { + if (stage) { + *stage = + SkNEW_ARGS(GrMagnifierEffect, (texture, + fSrcRect.x() / texture->width(), + fSrcRect.y() / texture->height(), + texture->width() / fSrcRect.width(), + texture->height() / fSrcRect.height(), + fInset / texture->width(), + fInset / texture->height())); + } + return true; +} + +void SkMagnifierImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const { + this->INHERITED::flatten(buffer); + buffer.writeScalar(fSrcRect.x()); + buffer.writeScalar(fSrcRect.y()); + buffer.writeScalar(fSrcRect.width()); + buffer.writeScalar(fSrcRect.height()); + buffer.writeScalar(fInset); +} + +bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src, + const SkMatrix&, SkBitmap* dst, + SkIPoint* offset) { + SkASSERT(src.config() == SkBitmap::kARGB_8888_Config); + SkASSERT(fSrcRect.width() < src.width()); + SkASSERT(fSrcRect.height() < src.height()); + + if (src.config() != SkBitmap::kARGB_8888_Config) { + return false; + } + + SkAutoLockPixels alp(src); + SkASSERT(src.getPixels()); + if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) { + return false; + } + + float inv_inset = fInset > 0 ? 1.0f / SkScalarToFloat(fInset) : 1.0f; + + float inv_x_zoom = fSrcRect.width() / src.width(); + float inv_y_zoom = fSrcRect.height() / src.height(); + + dst->setConfig(src.config(), src.width(), src.height()); + dst->allocPixels(); + SkColor* sptr = src.getAddr32(0, 0); + SkColor* dptr = dst->getAddr32(0, 0); + int width = src.width(), height = src.height(); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + float x_dist = std::min(x, width - x - 1) * inv_inset; + float y_dist = std::min(y, height - y - 1) * inv_inset; + float weight = 0; + + // To create a smooth curve at the corners, we need to work on + // a square twice the size of the inset. + if (x_dist < 2 && y_dist < 2) { + x_dist = 2 - x_dist; + y_dist = 2 - y_dist; + + float dist = sqrt(x_dist * x_dist + y_dist * y_dist); + dist = std::max(2 - dist, 0.0f); + weight = std::min(dist * dist, 1.0f); + } else { + float sq_dist = std::min(x_dist * x_dist, y_dist * y_dist); + weight = std::min(sq_dist, 1.0f); + } + + int x_val = weight * (fSrcRect.x() + x * inv_x_zoom) + + (1 - weight) * x; + int y_val = weight * (fSrcRect.y() + y * inv_y_zoom) + + (1 - weight) * y; + + x_val = std::min(x_val, width - 1); + y_val = std::min(y_val, height - 1); + + *dptr = sptr[y_val * width + x_val]; + dptr++; + } + } + return true; +} + +SK_DEFINE_FLATTENABLE_REGISTRAR(SkMagnifierImageFilter) diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp index 592cdfc6e6..2d6d3a2e0f 100644 --- a/src/gpu/SkGpuDevice.cpp +++ b/src/gpu/SkGpuDevice.cpp @@ -1966,4 +1966,3 @@ SkGpuDevice::SkGpuDevice(GrContext* context, fCache = cacheEntry; fNeedClear = needClear; } - diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp index 08e83dc05d..071f84a9e6 100644 --- a/src/ports/SkGlobalInitialization_chromium.cpp +++ b/src/ports/SkGlobalInitialization_chromium.cpp @@ -15,6 +15,7 @@ #include "SkLayerDrawLooper.h" #include "SkMallocPixelRef.h" #include "SkXfermode.h" +#include "SkZoomImageFilter.h" void SkFlattenable::InitializeFlattenables() { @@ -24,6 +25,7 @@ void SkFlattenable::InitializeFlattenables() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMallocPixelRef) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkZoomImageFilter) SkBlurMaskFilter::InitializeFlattenables(); SkColorFilter::InitializeFlattenables(); diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp index 69984a3406..9b7a74a00a 100644 --- a/src/ports/SkGlobalInitialization_default.cpp +++ b/src/ports/SkGlobalInitialization_default.cpp @@ -45,6 +45,7 @@ #include "SkStippleMaskFilter.h" #include "SkTableColorFilter.h" #include "SkTestImageFilters.h" +#include "SkZoomImageFilter.h" void SkFlattenable::InitializeFlattenables() { @@ -71,6 +72,7 @@ void SkFlattenable::InitializeFlattenables() { SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStippleMaskFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect) + SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkOffsetImageFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeImageFilter) diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp index 79c3faf0ee..888da2de09 100644 --- a/tests/GLProgramsTest.cpp +++ b/tests/GLProgramsTest.cpp @@ -205,11 +205,13 @@ DEFINE_GPUTESTCLASS("GLPrograms", GLProgramsTestClass, GLProgramsTest) // We force some of the effects that would otherwise be discarded to link here. #include "SkLightingImageFilter.h" +#include "SkMagnifierImageFilter.h" void forceLinking(); void forceLinking() { SkLightingImageFilter::CreateDistantLitDiffuse(SkPoint3(0,0,0), 0, 0, 0); + SkMagnifierImageFilter mag(SkRect::MakeWH(SK_Scalar1, SK_Scalar1), SK_Scalar1); } #endif |