SkBitmapFilter.cpp revision d647426714a96d42faff8ea53464343b29b427cd
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 "SkBitmapProcState.h"
9#include "SkBitmap.h"
10#include "SkColor.h"
11#include "SkColorPriv.h"
12#include "SkUnPreMultiply.h"
13#include "SkShader.h"
14#include "SkRTConf.h"
15#include "SkMath.h"
16
17void highQualityFilter(const SkBitmapProcState& s, int x, int y,
18                   SkPMColor* SK_RESTRICT colors, int count) {
19
20    const int maxX = s.fBitmap->width() - 1;
21    const int maxY = s.fBitmap->height() - 1;
22
23    while (count-- > 0) {
24        SkPoint srcPt;
25        s.fInvProc(*s.fInvMatrix, SkFloatToScalar(x + 0.5f),
26                    SkFloatToScalar(y + 0.5f), &srcPt);
27        srcPt.fX -= SK_ScalarHalf;
28        srcPt.fY -= SK_ScalarHalf;
29
30        SkFixed weight = 0;
31        SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
32
33        int y0 = SkClampMax(sk_float_ceil2int(SkScalarToFloat(srcPt.fY)-s.getBitmapFilter()->width()), maxY);
34        int y1 = SkClampMax(sk_float_floor2int(SkScalarToFloat(srcPt.fY)+s.getBitmapFilter()->width()), maxY);
35        int x0 = SkClampMax(sk_float_ceil2int(SkScalarToFloat(srcPt.fX)-s.getBitmapFilter()->width()), maxX);
36        int x1 = SkClampMax(sk_float_floor2int(SkScalarToFloat(srcPt.fX)+s.getBitmapFilter()->width()), maxX);
37
38        for (int src_y = y0; src_y <= y1; src_y++) {
39            SkFixed yweight = s.getBitmapFilter()->lookup((srcPt.fY - src_y));
40
41            for (int src_x = x0; src_x <= x1 ; src_x++) {
42                SkFixed xweight = s.getBitmapFilter()->lookup((srcPt.fX - src_x));
43
44                SkFixed combined_weight = SkFixedMul(xweight, yweight);
45
46                SkPMColor c = *s.fBitmap->getAddr32(src_x, src_y);
47                fr += combined_weight * SkGetPackedR32(c);
48                fg += combined_weight * SkGetPackedG32(c);
49                fb += combined_weight * SkGetPackedB32(c);
50                fa += combined_weight * SkGetPackedA32(c);
51                weight += combined_weight;
52            }
53        }
54
55        fr = SkFixedDiv(fr, weight);
56        fg = SkFixedDiv(fg, weight);
57        fb = SkFixedDiv(fb, weight);
58        fa = SkFixedDiv(fa, weight);
59
60        int a = SkClampMax(SkFixedRoundToInt(fa), 255);
61        int r = SkClampMax(SkFixedRoundToInt(fr), a);
62        int g = SkClampMax(SkFixedRoundToInt(fg), a);
63        int b = SkClampMax(SkFixedRoundToInt(fb), a);
64
65        *colors++ = SkPackARGB32(a, r, g, b);
66
67        x++;
68    }
69}
70
71void highQualityFilter_ScaleOnly(const SkBitmapProcState &s, int x, int y,
72                             SkPMColor *SK_RESTRICT colors, int count) {
73     const int maxX = s.fBitmap->width() - 1;
74     const int maxY = s.fBitmap->height() - 1;
75
76     SkPoint srcPt;
77
78     s.fInvProc(*s.fInvMatrix, SkFloatToScalar(x + 0.5f),
79                 SkFloatToScalar(y + 0.5f), &srcPt);
80     srcPt.fY -= SK_ScalarHalf;
81     int y0 = SkClampMax(sk_float_ceil2int(SkScalarToFloat(srcPt.fY)-s.getBitmapFilter()->width()), maxY);
82     int y1 = SkClampMax(sk_float_floor2int(SkScalarToFloat(srcPt.fY)+s.getBitmapFilter()->width()), maxY);
83
84     while (count-- > 0) {
85         s.fInvProc(*s.fInvMatrix, SkFloatToScalar(x + 0.5f),
86                     SkFloatToScalar(y + 0.5f), &srcPt);
87         srcPt.fX -= SK_ScalarHalf;
88         srcPt.fY -= SK_ScalarHalf;
89
90         SkFixed weight = 0;
91         SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
92
93         int x0 = SkClampMax(sk_float_ceil2int(SkScalarToFloat(srcPt.fX)-s.getBitmapFilter()->width()), maxX);
94         int x1 = SkClampMax(sk_float_floor2int(SkScalarToFloat(srcPt.fX)+s.getBitmapFilter()->width()), maxX);
95
96         for (int src_y = y0; src_y <= y1; src_y++) {
97             SkFixed yweight = s.getBitmapFilter()->lookup((srcPt.fY - src_y));
98
99             for (int src_x = x0; src_x <= x1 ; src_x++) {
100                 SkFixed xweight = s.getBitmapFilter()->lookup((srcPt.fX - src_x));
101
102                 SkFixed combined_weight = SkFixedMul(xweight, yweight);
103
104                 SkPMColor c = *s.fBitmap->getAddr32(src_x, src_y);
105                 fr += combined_weight * SkGetPackedR32(c);
106                 fg += combined_weight * SkGetPackedG32(c);
107                 fb += combined_weight * SkGetPackedB32(c);
108                 fa += combined_weight * SkGetPackedA32(c);
109                 weight += combined_weight;
110             }
111         }
112
113         fr = SkFixedDiv(fr, weight);
114         fg = SkFixedDiv(fg, weight);
115         fb = SkFixedDiv(fb, weight);
116         fa = SkFixedDiv(fa, weight);
117
118         int a = SkClampMax(SkFixedRoundToInt(fa), 255);
119         int r = SkClampMax(SkFixedRoundToInt(fr), a);
120         int g = SkClampMax(SkFixedRoundToInt(fg), a);
121         int b = SkClampMax(SkFixedRoundToInt(fb), a);
122
123         *colors++ = SkPackARGB32(a, r, g, b);
124
125         x++;
126     }
127}
128
129SK_CONF_DECLARE(const char *, c_bitmapFilter, "bitmap.filter", "mitchell", "Which bitmap filter to use [mitchell, sinc, gaussian, triangle, box]");
130
131static SkBitmapFilter *allocateBitmapFilter() {
132    if (!strcmp(c_bitmapFilter, "mitchell")) {
133        return SkNEW_ARGS(SkMitchellFilter,(1.f/3.f,1.f/3.f));
134    } else if (!strcmp(c_bitmapFilter, "sinc")) {
135        return SkNEW_ARGS(SkSincFilter,(3));
136    } else if (!strcmp(c_bitmapFilter, "gaussian")) {
137        return SkNEW_ARGS(SkGaussianFilter,(2));
138    } else if (!strcmp(c_bitmapFilter, "triangle")) {
139        return SkNEW(SkTriangleFilter);
140    } else if (!strcmp(c_bitmapFilter, "box")) {
141        return SkNEW(SkBoxFilter);
142    } else {
143        SkASSERT(!!!"Unknown filter type");
144    }
145
146    return NULL;
147}
148
149SkBitmapProcState::ShaderProc32
150SkBitmapProcState::chooseBitmapFilterProc(const SkPaint& paint) {
151    // we need to be requested
152    uint32_t mask = SkPaint::kFilterBitmap_Flag
153                  | SkPaint::kHighQualityFilterBitmap_Flag
154                  ;
155    if ((paint.getFlags() & mask) != mask) {
156        return NULL;
157    }
158
159    // TODO: consider supporting other configs (e.g. 565, A8)
160    if (fBitmap->config() != SkBitmap::kARGB_8888_Config) {
161        return NULL;
162    }
163
164    // TODO: consider supporting repeat and mirror
165    if (SkShader::kClamp_TileMode != fTileModeX || SkShader::kClamp_TileMode != fTileModeY) {
166        return NULL;
167    }
168
169    // TODO: support blending inside our procs
170    if (0xFF != paint.getAlpha()) {
171        return NULL;
172    }
173
174    if (fInvType & (SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask)) {
175        fBitmapFilter = allocateBitmapFilter();
176    }
177
178    if (fInvType & SkMatrix::kAffine_Mask) {
179        return highQualityFilter;
180    } else if (fInvType & SkMatrix::kScale_Mask) {
181        return highQualityFilter_ScaleOnly;
182    } else {
183        return NULL;
184    }
185}
186
187static void divideByWeights(SkFixed *sums, SkFixed *weights, SkBitmap *dst) {
188    for (int y = 0 ; y < dst->height() ; y++) {
189        for (int x = 0 ; x < dst->width() ; x++) {
190            SkFixed fr = SkFixedDiv(sums[4*(y*dst->width() + x) + 0], weights[y*dst->width() + x]);
191            SkFixed fg = SkFixedDiv(sums[4*(y*dst->width() + x) + 1], weights[y*dst->width() + x]);
192            SkFixed fb = SkFixedDiv(sums[4*(y*dst->width() + x) + 2], weights[y*dst->width() + x]);
193            SkFixed fa = SkFixedDiv(sums[4*(y*dst->width() + x) + 3], weights[y*dst->width() + x]);
194            int a = SkClampMax(SkFixedRoundToInt(fa), 255);
195            int r = SkClampMax(SkFixedRoundToInt(fr), a);
196            int g = SkClampMax(SkFixedRoundToInt(fg), a);
197            int b = SkClampMax(SkFixedRoundToInt(fb), a);
198
199            *dst->getAddr32(x,y) = SkPackARGB32(a, r, g, b);
200        }
201    }
202}
203
204static void upScaleHoriz(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
205    for (int y = 0 ; y < src->height() ; y++) {
206        for (int x = 0 ; x < dst->width() ; x++) {
207            float sx = (x + 0.5f) / scale - 0.5f;
208            int x0 = SkClampMax(sk_float_ceil2int(sx-filter->width()), src->width()-1);
209            int x1 = SkClampMax(sk_float_floor2int(sx+filter->width()), src->width()-1);
210
211            SkFixed total_weight = 0;
212            SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
213
214            for (int src_x = x0 ; src_x <= x1 ; src_x++) {
215                SkFixed weight = filter->lookup(sx - src_x);
216                SkPMColor c = *src->getAddr32(src_x,y);
217                fr += weight * SkGetPackedR32(c);
218                fg += weight * SkGetPackedG32(c);
219                fb += weight * SkGetPackedB32(c);
220                fa += weight * SkGetPackedA32(c);
221                total_weight += weight;
222            }
223            fr = SkFixedDiv(fr, total_weight);
224            fg = SkFixedDiv(fg, total_weight);
225            fb = SkFixedDiv(fb, total_weight);
226            fa = SkFixedDiv(fa, total_weight);
227
228            int a = SkClampMax(SkFixedRoundToInt(fa), 255);
229            int r = SkClampMax(SkFixedRoundToInt(fr), a);
230            int g = SkClampMax(SkFixedRoundToInt(fg), a);
231            int b = SkClampMax(SkFixedRoundToInt(fb), a);
232
233            *dst->getAddr32(x,y) = SkPackARGB32(a, r, g, b);
234        }
235    }
236}
237
238static void downScaleHoriz(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
239    SkFixed *sums = SkNEW_ARRAY(SkFixed, dst->width() * dst->height() * 4);
240    SkFixed *weights = SkNEW_ARRAY(SkFixed, dst->width() * dst->height());
241
242    SkAutoTDeleteArray<SkFixed> ada1(sums);
243    SkAutoTDeleteArray<SkFixed> ada2(weights);
244
245    memset(sums, 0, dst->width() * dst->height() * sizeof(SkFixed) * 4);
246    memset(weights, 0, dst->width() * dst->height() * sizeof(SkFixed));
247
248    for (int y = 0 ; y < src->height() ; y++) {
249        for (int x = 0 ; x < src->width() ; x++) {
250            // splat each source pixel into the destination image
251            float dx = (x + 0.5f) * scale - 0.5f;
252            int x0 = SkClampMax(sk_float_ceil2int(dx-filter->width()), dst->width()-1);
253            int x1 = SkClampMax(sk_float_floor2int(dx+filter->width()), dst->width()-1);
254
255            SkPMColor c = *src->getAddr32(x,y);
256
257            for (int dst_x = x0 ; dst_x <= x1 ; dst_x++) {
258                SkFixed weight = filter->lookup(dx - dst_x);
259                sums[4*(y*dst->width() + dst_x) + 0] += weight*SkGetPackedR32(c);
260                sums[4*(y*dst->width() + dst_x) + 1] += weight*SkGetPackedG32(c);
261                sums[4*(y*dst->width() + dst_x) + 2] += weight*SkGetPackedB32(c);
262                sums[4*(y*dst->width() + dst_x) + 3] += weight*SkGetPackedA32(c);
263                weights[y*dst->width() + dst_x] += weight;
264            }
265        }
266    }
267
268    divideByWeights(sums, weights, dst);
269}
270
271static void upScaleVert(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
272    for (int y = 0 ; y < dst->height() ; y++) {
273        for (int x = 0 ; x < dst->width() ; x++) {
274            float sy = (y + 0.5f) / scale - 0.5f;
275            int y0 = SkClampMax(sk_float_ceil2int(sy-filter->width()), src->height()-1);
276            int y1 = SkClampMax(sk_float_floor2int(sy+filter->width()), src->height()-1);
277
278            SkFixed total_weight = 0;
279            SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
280
281            for (int src_y = y0 ; src_y <= y1 ; src_y++) {
282                SkFixed weight = filter->lookup(sy - src_y);
283                SkPMColor c = *src->getAddr32(x,src_y);
284                fr += weight * SkGetPackedR32(c);
285                fg += weight * SkGetPackedG32(c);
286                fb += weight * SkGetPackedB32(c);
287                fa += weight * SkGetPackedA32(c);
288                total_weight += weight;
289            }
290            fr = SkFixedDiv(fr, total_weight);
291            fg = SkFixedDiv(fg, total_weight);
292            fb = SkFixedDiv(fb, total_weight);
293            fa = SkFixedDiv(fa, total_weight);
294
295            int a = SkClampMax(SkFixedRoundToInt(fa), 255);
296            int r = SkClampMax(SkFixedRoundToInt(fr), a);
297            int g = SkClampMax(SkFixedRoundToInt(fg), a);
298            int b = SkClampMax(SkFixedRoundToInt(fb), a);
299
300            *dst->getAddr32(x,y) = SkPackARGB32(a, r, g, b);
301        }
302    }
303}
304
305static void downScaleVert(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
306    SkFixed *sums = SkNEW_ARRAY(SkFixed, dst->width() * dst->height() * 4);
307    SkFixed *weights = SkNEW_ARRAY(SkFixed, dst->width() * dst->height());
308
309    SkAutoTDeleteArray<SkFixed> ada1(sums);
310    SkAutoTDeleteArray<SkFixed> ada2(weights);
311
312    memset(sums, 0, dst->width() * dst->height() * sizeof(SkFixed) * 4);
313    memset(weights, 0, dst->width() * dst->height() * sizeof(SkFixed));
314
315    for (int y = 0 ; y < src->height() ; y++) {
316        for (int x = 0 ; x < src->width() ; x++) {
317            // splat each source pixel into the destination image
318            float dy = (y + 0.5f) * scale - 0.5f;
319            int y0 = SkClampMax(sk_float_ceil2int(dy-filter->width()), dst->height()-1);
320            int y1 = SkClampMax(sk_float_ceil2int(dy+filter->width()), dst->height()-1);
321
322            SkPMColor c = *src->getAddr32(x,y);
323
324            for (int dst_y = y0 ; dst_y <= y1 ; dst_y++) {
325                SkFixed weight = filter->lookup(dy - dst_y);
326                sums[4*(dst_y*dst->width() + x) + 0] += weight*SkGetPackedR32(c);
327                sums[4*(dst_y*dst->width() + x) + 1] += weight*SkGetPackedG32(c);
328                sums[4*(dst_y*dst->width() + x) + 2] += weight*SkGetPackedB32(c);
329                sums[4*(dst_y*dst->width() + x) + 3] += weight*SkGetPackedA32(c);
330                weights[dst_y*dst->width() + x] += weight;
331            }
332        }
333    }
334
335    divideByWeights(sums, weights, dst);
336}
337
338void SkBitmap::scale(SkBitmap *dst) const {
339
340    SkBitmap horiz_temp;
341
342    horiz_temp.setConfig(SkBitmap::kARGB_8888_Config, dst->width(), height());
343    horiz_temp.allocPixels();
344
345    SkBitmapFilter *filter = allocateBitmapFilter();
346
347    float horiz_scale = float(dst->width()) / width();
348
349    if (horiz_scale == 1) {
350        this->copyPixelsTo(horiz_temp.getPixels(), getSize());
351    } else if (horiz_scale > 1) {
352        upScaleHoriz(this, &horiz_temp, horiz_scale, filter);
353    } else if (horiz_scale < 1) {
354        downScaleHoriz(this, &horiz_temp, horiz_scale, filter);
355    }
356
357    float vert_scale = float(dst->height()) / height();
358
359    if (vert_scale == 1) {
360        horiz_temp.copyPixelsTo(dst->getPixels(), dst->getSize());
361    } else if (vert_scale > 1) {
362        upScaleVert(&horiz_temp, dst, vert_scale, filter);
363    } else if (vert_scale < 1) {
364        downScaleVert(&horiz_temp, dst, vert_scale, filter);
365    }
366
367    SkDELETE(filter);
368}
369