SkBitmapFilter.cpp revision d923288e50b5a69afb0bdd5c161191b24cab8345
1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkErrorInternals.h"
9#include "SkConvolver.h"
10#include "SkBitmapProcState.h"
11#include "SkBitmap.h"
12#include "SkColor.h"
13#include "SkColorPriv.h"
14#include "SkConvolver.h"
15#include "SkUnPreMultiply.h"
16#include "SkShader.h"
17#include "SkRTConf.h"
18#include "SkMath.h"
19
20// These are the per-scanline callbacks that are used when we must resort to
21// resampling an image as it is blitted.  Typically these are used only when
22// the image is rotated or has some other complex transformation applied.
23// Scaled images will usually be rescaled directly before rasterization.
24
25namespace {
26
27template <typename Color, typename ColorPacker>
28void highQualityFilter(ColorPacker pack, const SkBitmapProcState& s, int x, int y, Color* SK_RESTRICT colors, int count) {
29    const int maxX = s.fBitmap->width();
30    const int maxY = s.fBitmap->height();
31
32    while (count-- > 0) {
33        SkPoint srcPt;
34        s.fInvProc(s.fInvMatrix, x + 0.5f,
35                    y + 0.5f, &srcPt);
36        srcPt.fX -= SK_ScalarHalf;
37        srcPt.fY -= SK_ScalarHalf;
38
39        SkScalar weight = 0;
40        SkScalar fr = 0, fg = 0, fb = 0, fa = 0;
41
42        int y0 = SkClampMax(SkScalarCeilToInt(srcPt.fY-s.getBitmapFilter()->width()), maxY);
43        int y1 = SkClampMax(SkScalarFloorToInt(srcPt.fY+s.getBitmapFilter()->width()+1), maxY);
44        int x0 = SkClampMax(SkScalarCeilToInt(srcPt.fX-s.getBitmapFilter()->width()), maxX);
45        int x1 = SkClampMax(SkScalarFloorToInt(srcPt.fX+s.getBitmapFilter()->width())+1, maxX);
46
47        for (int srcY = y0; srcY < y1; srcY++) {
48            SkScalar yWeight = s.getBitmapFilter()->lookupScalar((srcPt.fY - srcY));
49
50            for (int srcX = x0; srcX < x1 ; srcX++) {
51                SkScalar xWeight = s.getBitmapFilter()->lookupScalar((srcPt.fX - srcX));
52
53                SkScalar combined_weight = SkScalarMul(xWeight, yWeight);
54
55                SkPMColor c = *s.fBitmap->getAddr32(srcX, srcY);
56                fr += combined_weight * SkGetPackedR32(c);
57                fg += combined_weight * SkGetPackedG32(c);
58                fb += combined_weight * SkGetPackedB32(c);
59                fa += combined_weight * SkGetPackedA32(c);
60                weight += combined_weight;
61            }
62        }
63
64        fr = SkScalarDiv(fr, weight);
65        fg = SkScalarDiv(fg, weight);
66        fb = SkScalarDiv(fb, weight);
67        fa = SkScalarDiv(fa, weight);
68
69        int a = SkClampMax(SkScalarRoundToInt(fa), 255);
70        int r = SkClampMax(SkScalarRoundToInt(fr), a);
71        int g = SkClampMax(SkScalarRoundToInt(fg), a);
72        int b = SkClampMax(SkScalarRoundToInt(fb), a);
73
74        *colors++ = pack(a, r, g, b);
75
76        x++;
77    }
78}
79
80uint16_t PackTo565(int /*a*/, int r, int g, int b) {
81    return SkPack888ToRGB16(r, g, b);
82}
83
84}  // namespace
85
86void highQualityFilter32(const SkBitmapProcState& s, int x, int y, SkPMColor* SK_RESTRICT colors, int count) {
87    highQualityFilter(&SkPackARGB32, s, x, y, colors, count);
88}
89
90void highQualityFilter16(const SkBitmapProcState& s, int x, int y, uint16_t* SK_RESTRICT colors, int count) {
91    highQualityFilter(&PackTo565, s, x, y, colors, count);
92}
93
94
95SK_CONF_DECLARE(const char *, c_bitmapFilter, "bitmap.filter", "mitchell", "Which scanline bitmap filter to use [mitchell, lanczos, hamming, gaussian, triangle, box]");
96
97SkBitmapFilter *SkBitmapFilter::Allocate() {
98    if (!strcmp(c_bitmapFilter, "mitchell")) {
99        return SkNEW_ARGS(SkMitchellFilter,(1.f/3.f,1.f/3.f));
100    } else if (!strcmp(c_bitmapFilter, "lanczos")) {
101        return SkNEW(SkLanczosFilter);
102    } else if (!strcmp(c_bitmapFilter, "hamming")) {
103        return SkNEW(SkHammingFilter);
104    } else if (!strcmp(c_bitmapFilter, "gaussian")) {
105        return SkNEW_ARGS(SkGaussianFilter,(2));
106    } else if (!strcmp(c_bitmapFilter, "triangle")) {
107        return SkNEW(SkTriangleFilter);
108    } else if (!strcmp(c_bitmapFilter, "box")) {
109        return SkNEW(SkBoxFilter);
110    } else {
111        SkDEBUGFAIL("Unknown filter type");
112    }
113
114    return NULL;
115}
116
117bool SkBitmapProcState::setBitmapFilterProcs() {
118    if (fFilterLevel != SkPaint::kHigh_FilterLevel) {
119        return false;
120    }
121
122    if (fAlphaScale != 256) {
123        return false;
124    }
125
126    // TODO: consider supporting other colortypes (e.g. 565, A8)
127    if (fBitmap->colorType() != kN32_SkColorType) {
128        return false;
129    }
130
131    // TODO: consider supporting repeat and mirror
132    if (SkShader::kClamp_TileMode != fTileModeX || SkShader::kClamp_TileMode != fTileModeY) {
133        return false;
134    }
135
136    // TODO: is this right?  do we want fBitmapFilter allocated even if we can't set shader procs?
137    if (fInvType & (SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask)) {
138        fBitmapFilter = SkBitmapFilter::Allocate();
139    }
140
141    if (fInvType & SkMatrix::kScale_Mask) {
142        fShaderProc32 = highQualityFilter32;
143        fShaderProc16 = highQualityFilter16;
144        return true;
145    } else {
146        return false;
147    }
148}
149