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
25void highQualityFilter(const SkBitmapProcState& s, int x, int y,
26                   SkPMColor* SK_RESTRICT colors, int count) {
27
28    const int maxX = s.fBitmap->width() - 1;
29    const int maxY = s.fBitmap->height() - 1;
30
31    while (count-- > 0) {
32        SkPoint srcPt;
33        s.fInvProc(s.fInvMatrix, SkFloatToScalar(x + 0.5f),
34                    SkFloatToScalar(y + 0.5f), &srcPt);
35        srcPt.fX -= SK_ScalarHalf;
36        srcPt.fY -= SK_ScalarHalf;
37
38        SkScalar weight = 0;
39        SkScalar fr = 0, fg = 0, fb = 0, fa = 0;
40
41        int y0 = SkClampMax(SkScalarCeilToInt(srcPt.fY-s.getBitmapFilter()->width()), maxY);
42        int y1 = SkClampMax(SkScalarFloorToInt(srcPt.fY+s.getBitmapFilter()->width()), maxY);
43        int x0 = SkClampMax(SkScalarCeilToInt(srcPt.fX-s.getBitmapFilter()->width()), maxX);
44        int x1 = SkClampMax(SkScalarFloorToInt(srcPt.fX+s.getBitmapFilter()->width()), maxX);
45
46        for (int srcY = y0; srcY <= y1; srcY++) {
47            SkScalar yWeight = s.getBitmapFilter()->lookupScalar((srcPt.fY - srcY));
48
49            for (int srcX = x0; srcX <= x1 ; srcX++) {
50                SkScalar xWeight = s.getBitmapFilter()->lookupScalar((srcPt.fX - srcX));
51
52                SkScalar combined_weight = SkScalarMul(xWeight, yWeight);
53
54                SkPMColor c = *s.fBitmap->getAddr32(srcX, srcY);
55                fr += combined_weight * SkGetPackedR32(c);
56                fg += combined_weight * SkGetPackedG32(c);
57                fb += combined_weight * SkGetPackedB32(c);
58                fa += combined_weight * SkGetPackedA32(c);
59                weight += combined_weight;
60            }
61        }
62
63        fr = SkScalarDiv(fr, weight);
64        fg = SkScalarDiv(fg, weight);
65        fb = SkScalarDiv(fb, weight);
66        fa = SkScalarDiv(fa, weight);
67
68        int a = SkClampMax(SkScalarRoundToInt(fa), 255);
69        int r = SkClampMax(SkScalarRoundToInt(fr), a);
70        int g = SkClampMax(SkScalarRoundToInt(fg), a);
71        int b = SkClampMax(SkScalarRoundToInt(fb), a);
72
73        *colors++ = SkPackARGB32(a, r, g, b);
74
75        x++;
76    }
77}
78
79SK_CONF_DECLARE(const char *, c_bitmapFilter, "bitmap.filter", "mitchell", "Which scanline bitmap filter to use [mitchell, lanczos, hamming, gaussian, triangle, box]");
80
81SkBitmapFilter *SkBitmapFilter::Allocate() {
82    if (!strcmp(c_bitmapFilter, "mitchell")) {
83        return SkNEW_ARGS(SkMitchellFilter,(1.f/3.f,1.f/3.f));
84    } else if (!strcmp(c_bitmapFilter, "lanczos")) {
85        return SkNEW(SkLanczosFilter);
86    } else if (!strcmp(c_bitmapFilter, "hamming")) {
87        return SkNEW(SkHammingFilter);
88    } else if (!strcmp(c_bitmapFilter, "gaussian")) {
89        return SkNEW_ARGS(SkGaussianFilter,(2));
90    } else if (!strcmp(c_bitmapFilter, "triangle")) {
91        return SkNEW(SkTriangleFilter);
92    } else if (!strcmp(c_bitmapFilter, "box")) {
93        return SkNEW(SkBoxFilter);
94    } else {
95        SkASSERT(!!!"Unknown filter type");
96    }
97
98    return NULL;
99}
100
101SkBitmapProcState::ShaderProc32
102SkBitmapProcState::chooseBitmapFilterProc() {
103
104    if (fFilterLevel != SkPaint::kHigh_FilterLevel) {
105        return NULL;
106    }
107
108    if (fAlphaScale != 256) {
109        return NULL;
110    }
111
112    // TODO: consider supporting other configs (e.g. 565, A8)
113    if (fBitmap->config() != SkBitmap::kARGB_8888_Config) {
114        return NULL;
115    }
116
117    // TODO: consider supporting repeat and mirror
118    if (SkShader::kClamp_TileMode != fTileModeX || SkShader::kClamp_TileMode != fTileModeY) {
119        return NULL;
120    }
121
122    if (fInvType & (SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask)) {
123        fBitmapFilter = SkBitmapFilter::Allocate();
124    }
125
126    if (fInvType & SkMatrix::kScale_Mask) {
127        return highQualityFilter;
128    } else {
129        return NULL;
130    }
131}
132