aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--gm/imagemagnifier.cpp63
-rw-r--r--gyp/effects.gyp2
-rw-r--r--gyp/gmslides.gypi1
-rw-r--r--include/effects/SkMagnifierImageFilter.h37
-rw-r--r--include/gpu/GrContext.h17
-rw-r--r--src/effects/SkMagnifierImageFilter.cpp331
-rw-r--r--src/gpu/SkGpuDevice.cpp1
-rw-r--r--src/ports/SkGlobalInitialization_chromium.cpp2
-rw-r--r--src/ports/SkGlobalInitialization_default.cpp2
-rw-r--r--tests/GLProgramsTest.cpp2
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