blob: 441ad05df0c91ec24ba7906a275803cdf757391a [file] [log] [blame]
humper@google.comb0889472013-07-09 21:37:14 +00001/*
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, SkIntToScalar(x),
26 SkIntToScalar(y), &srcPt);
27 srcPt.fX -= SK_ScalarHalf;
28 srcPt.fY -= SK_ScalarHalf;
29
30 int sx = SkScalarFloorToInt(srcPt.fX);
31 int sy = SkScalarFloorToInt(srcPt.fY);
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000032
humper@google.comb0889472013-07-09 21:37:14 +000033 SkFixed weight = 0;
34 SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000035
humper@google.comb0889472013-07-09 21:37:14 +000036 int y0 = SkClampMax(int(ceil(sy-s.getBitmapFilter()->width() + 0.5f)), maxY);
37 int y1 = SkClampMax(int(floor(sy+s.getBitmapFilter()->width() + 0.5f)), maxY);
38 int x0 = SkClampMax(int(ceil(sx-s.getBitmapFilter()->width() + 0.5f)), maxX);
39 int x1 = SkClampMax(int(floor(sx+s.getBitmapFilter()->width() + 0.5f)), maxX);
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000040
humper@google.comb0889472013-07-09 21:37:14 +000041 for (int src_y = y0; src_y <= y1; src_y++) {
42 SkFixed yweight = s.getBitmapFilter()->lookup((srcPt.fY - src_y));
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000043
humper@google.comb0889472013-07-09 21:37:14 +000044 for (int src_x = x0; src_x <= x1 ; src_x++) {
45 SkFixed xweight = s.getBitmapFilter()->lookup((srcPt.fX - src_x));
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000046
humper@google.comb0889472013-07-09 21:37:14 +000047 SkFixed combined_weight = SkFixedMul(xweight, yweight);
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000048
humper@google.comb0889472013-07-09 21:37:14 +000049 SkPMColor c = *s.fBitmap->getAddr32(src_x, src_y);
50 fr += combined_weight * SkGetPackedR32(c);
51 fg += combined_weight * SkGetPackedG32(c);
52 fb += combined_weight * SkGetPackedB32(c);
53 fa += combined_weight * SkGetPackedA32(c);
54 weight += combined_weight;
55 }
56 }
57
58 fr = SkFixedDiv(fr, weight);
59 fg = SkFixedDiv(fg, weight);
60 fb = SkFixedDiv(fb, weight);
61 fa = SkFixedDiv(fa, weight);
62
63 int a = SkClampMax(SkFixedRoundToInt(fa), 255);
64 int r = SkClampMax(SkFixedRoundToInt(fr), a);
65 int g = SkClampMax(SkFixedRoundToInt(fg), a);
66 int b = SkClampMax(SkFixedRoundToInt(fb), a);
67
68 *colors++ = SkPackARGB32(a, r, g, b);
69
70 x++;
71 }
72}
73
74void highQualityFilter_ScaleOnly(const SkBitmapProcState &s, int x, int y,
75 SkPMColor *SK_RESTRICT colors, int count) {
76 const int maxX = s.fBitmap->width() - 1;
77 const int maxY = s.fBitmap->height() - 1;
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000078
humper@google.comb0889472013-07-09 21:37:14 +000079 SkPoint srcPt;
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +000080
humper@google.comb0889472013-07-09 21:37:14 +000081 s.fInvProc(*s.fInvMatrix, SkIntToScalar(x),
82 SkIntToScalar(y), &srcPt);
83 srcPt.fY -= SK_ScalarHalf;
84 int sy = SkScalarFloorToInt(srcPt.fY);
85 int y0 = SkClampMax(int(ceil(sy-s.getBitmapFilter()->width() + 0.5f)), maxY);
86 int y1 = SkClampMax(int(floor(sy+s.getBitmapFilter()->width() + 0.5f)), maxY);
87
88 while (count-- > 0) {
89 s.fInvProc(*s.fInvMatrix, SkIntToScalar(x),
90 SkIntToScalar(y), &srcPt);
91 srcPt.fX -= SK_ScalarHalf;
92 srcPt.fY -= SK_ScalarHalf;
93
94 int sx = SkScalarFloorToInt(srcPt.fX);
95
96 SkFixed weight = 0;
97 SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
98
99 int x0 = SkClampMax(int(ceil(sx-s.getBitmapFilter()->width() + 0.5f)), maxX);
100 int x1 = SkClampMax(int(floor(sx+s.getBitmapFilter()->width() + 0.5f)), maxX);
101
102 for (int src_y = y0; src_y <= y1; src_y++) {
103 SkFixed yweight = s.getBitmapFilter()->lookup((srcPt.fY - src_y));
104
105 for (int src_x = x0; src_x <= x1 ; src_x++) {
106 SkFixed xweight = s.getBitmapFilter()->lookup((srcPt.fX - src_x));
107
108 SkFixed combined_weight = SkFixedMul(xweight, yweight);
109
110 SkPMColor c = *s.fBitmap->getAddr32(src_x, src_y);
111 fr += combined_weight * SkGetPackedR32(c);
112 fg += combined_weight * SkGetPackedG32(c);
113 fb += combined_weight * SkGetPackedB32(c);
114 fa += combined_weight * SkGetPackedA32(c);
115 weight += combined_weight;
116 }
117 }
118
119 fr = SkFixedDiv(fr, weight);
120 fg = SkFixedDiv(fg, weight);
121 fb = SkFixedDiv(fb, weight);
122 fa = SkFixedDiv(fa, weight);
123
124 int a = SkClampMax(SkFixedRoundToInt(fa), 255);
125 int r = SkClampMax(SkFixedRoundToInt(fr), a);
126 int g = SkClampMax(SkFixedRoundToInt(fg), a);
127 int b = SkClampMax(SkFixedRoundToInt(fb), a);
128
129 *colors++ = SkPackARGB32(a, r, g, b);
130
131 x++;
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000132 }
humper@google.comb0889472013-07-09 21:37:14 +0000133}
134
135SK_CONF_DECLARE(const char *, c_bitmapFilter, "bitmap.filter", "mitchell", "Which bitmap filter to use [mitchell, sinc, gaussian, triangle, box]");
136
137static SkBitmapFilter *allocateBitmapFilter() {
138 if (!strcmp(c_bitmapFilter, "mitchell")) {
139 return SkNEW_ARGS(SkMitchellFilter,(1.f/3.f,1.f/3.f));
140 } else if (!strcmp(c_bitmapFilter, "sinc")) {
141 return SkNEW_ARGS(SkSincFilter,(3));
142 } else if (!strcmp(c_bitmapFilter, "gaussian")) {
143 return SkNEW_ARGS(SkGaussianFilter,(2));
144 } else if (!strcmp(c_bitmapFilter, "triangle")) {
145 return SkNEW(SkTriangleFilter);
146 } else if (!strcmp(c_bitmapFilter, "box")) {
147 return SkNEW(SkBoxFilter);
148 } else {
149 SkASSERT(!!!"Unknown filter type");
150 }
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000151
humper@google.comb0889472013-07-09 21:37:14 +0000152 return NULL;
153}
154
155SkBitmapProcState::ShaderProc32
156SkBitmapProcState::chooseBitmapFilterProc(const SkPaint& paint) {
157 // we need to be requested
158 uint32_t mask = SkPaint::kFilterBitmap_Flag
159 | SkPaint::kHighQualityFilterBitmap_Flag
160 ;
161 if ((paint.getFlags() & mask) != mask) {
162 return NULL;
163 }
164
165 // TODO: consider supporting other configs (e.g. 565, A8)
166 if (fBitmap->config() != SkBitmap::kARGB_8888_Config) {
167 return NULL;
168 }
169
170 // TODO: consider supporting repeat and mirror
171 if (SkShader::kClamp_TileMode != fTileModeX || SkShader::kClamp_TileMode != fTileModeY) {
172 return NULL;
173 }
174
175 // TODO: support blending inside our procs
176 if (0xFF != paint.getAlpha()) {
177 return NULL;
178 }
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000179
humper@google.comb0889472013-07-09 21:37:14 +0000180 if (fInvType & (SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask)) {
181 fBitmapFilter = allocateBitmapFilter();
182 }
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000183
humper@google.comb0889472013-07-09 21:37:14 +0000184 if (fInvType & SkMatrix::kAffine_Mask) {
185 return highQualityFilter;
186 } else if (fInvType & SkMatrix::kScale_Mask) {
187 return highQualityFilter_ScaleOnly;
188 } else {
189 return NULL;
190 }
191}
192
193static void divideByWeights(SkFixed *sums, SkFixed *weights, SkBitmap *dst) {
194 for (int y = 0 ; y < dst->height() ; y++) {
195 for (int x = 0 ; x < dst->width() ; x++) {
196 SkFixed fr = SkFixedDiv(sums[4*(y*dst->width() + x) + 0], weights[y*dst->width() + x]);
197 SkFixed fg = SkFixedDiv(sums[4*(y*dst->width() + x) + 1], weights[y*dst->width() + x]);
198 SkFixed fb = SkFixedDiv(sums[4*(y*dst->width() + x) + 2], weights[y*dst->width() + x]);
199 SkFixed fa = SkFixedDiv(sums[4*(y*dst->width() + x) + 3], weights[y*dst->width() + x]);
200 int a = SkClampMax(SkFixedRoundToInt(fa), 255);
201 int r = SkClampMax(SkFixedRoundToInt(fr), a);
202 int g = SkClampMax(SkFixedRoundToInt(fg), a);
203 int b = SkClampMax(SkFixedRoundToInt(fb), a);
204
205 *dst->getAddr32(x,y) = SkPackARGB32(a, r, g, b);
206 }
207 }
208}
209
210static void upScaleHoriz(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
211 for (int y = 0 ; y < src->height() ; y++) {
212 for (int x = 0 ; x < dst->width() ; x++) {
213 float sx = x / scale - 0.5f;
214 int x0 = SkClampMax(int(ceil(sx-filter->width() + 0.5f)), src->width()-1);
215 int x1 = SkClampMax(int(floor(sx+filter->width() + 0.5f)), src->width()-1);
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000216
humper@google.comb0889472013-07-09 21:37:14 +0000217 SkFixed total_weight = 0;
218 SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000219
humper@google.comb0889472013-07-09 21:37:14 +0000220 for (int src_x = x0 ; src_x <= x1 ; src_x++) {
221 SkFixed weight = filter->lookup(sx - src_x);
222 SkPMColor c = *src->getAddr32(src_x,y);
223 fr += weight * SkGetPackedR32(c);
224 fg += weight * SkGetPackedG32(c);
225 fb += weight * SkGetPackedB32(c);
226 fa += weight * SkGetPackedA32(c);
227 total_weight += weight;
228 }
229 fr = SkFixedDiv(fr, total_weight);
230 fg = SkFixedDiv(fg, total_weight);
231 fb = SkFixedDiv(fb, total_weight);
232 fa = SkFixedDiv(fa, total_weight);
233
234 int a = SkClampMax(SkFixedRoundToInt(fa), 255);
235 int r = SkClampMax(SkFixedRoundToInt(fr), a);
236 int g = SkClampMax(SkFixedRoundToInt(fg), a);
237 int b = SkClampMax(SkFixedRoundToInt(fb), a);
238
239 *dst->getAddr32(x,y) = SkPackARGB32(a, r, g, b);
240 }
241 }
242}
243
244static void downScaleHoriz(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
245 SkFixed *sums = SkNEW_ARRAY(SkFixed, dst->width() * dst->height() * 4);
246 SkFixed *weights = SkNEW_ARRAY(SkFixed, dst->width() * dst->height());
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000247
humper@google.comb0889472013-07-09 21:37:14 +0000248 SkAutoTDeleteArray<SkFixed> ada1(sums);
249 SkAutoTDeleteArray<SkFixed> ada2(weights);
250
251 memset(sums, 0, dst->width() * dst->height() * sizeof(SkFixed) * 4);
252 memset(weights, 0, dst->width() * dst->height() * sizeof(SkFixed));
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000253
humper@google.comb0889472013-07-09 21:37:14 +0000254 for (int y = 0 ; y < src->height() ; y++) {
255 for (int x = 0 ; x < src->width() ; x++) {
256 // splat each source pixel into the destination image
257 float dx = (x + 0.5f) * scale;
258 int x0 = SkClampMax(int(ceil(dx-filter->width() + 0.5f)), dst->width()-1);
259 int x1 = SkClampMax(int(floor(dx+filter->width() + 0.5f)), dst->width()-1);
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000260
humper@google.comb0889472013-07-09 21:37:14 +0000261 SkPMColor c = *src->getAddr32(x,y);
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000262
humper@google.comb0889472013-07-09 21:37:14 +0000263 for (int dst_x = x0 ; dst_x <= x1 ; dst_x++) {
264 SkFixed weight = filter->lookup(dx - dst_x);
265 sums[4*(y*dst->width() + dst_x) + 0] += weight*SkGetPackedR32(c);
266 sums[4*(y*dst->width() + dst_x) + 1] += weight*SkGetPackedG32(c);
267 sums[4*(y*dst->width() + dst_x) + 2] += weight*SkGetPackedB32(c);
268 sums[4*(y*dst->width() + dst_x) + 3] += weight*SkGetPackedA32(c);
269 weights[y*dst->width() + dst_x] += weight;
270 }
271 }
272 }
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000273
humper@google.comb0889472013-07-09 21:37:14 +0000274 divideByWeights(sums, weights, dst);
275}
276
277static void upScaleVert(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
278 for (int y = 0 ; y < dst->height() ; y++) {
279 for (int x = 0 ; x < dst->width() ; x++) {
280 float sy = y / scale - 0.5f;
281 int y0 = SkClampMax(int(ceil(sy-filter->width() + 0.5f)), src->height()-1);
282 int y1 = SkClampMax(int(floor(sy+filter->width() + 0.5f)), src->height()-1);
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000283
humper@google.comb0889472013-07-09 21:37:14 +0000284 SkFixed total_weight = 0;
285 SkFixed fr = 0, fg = 0, fb = 0, fa = 0;
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000286
humper@google.comb0889472013-07-09 21:37:14 +0000287 for (int src_y = y0 ; src_y <= y1 ; src_y++) {
288 SkFixed weight = filter->lookup(sy - src_y);
289 SkPMColor c = *src->getAddr32(x,src_y);
290 fr += weight * SkGetPackedR32(c);
291 fg += weight * SkGetPackedG32(c);
292 fb += weight * SkGetPackedB32(c);
293 fa += weight * SkGetPackedA32(c);
294 total_weight += weight;
295 }
296 fr = SkFixedDiv(fr, total_weight);
297 fg = SkFixedDiv(fg, total_weight);
298 fb = SkFixedDiv(fb, total_weight);
299 fa = SkFixedDiv(fa, total_weight);
300
301 int a = SkClampMax(SkFixedRoundToInt(fa), 255);
302 int r = SkClampMax(SkFixedRoundToInt(fr), a);
303 int g = SkClampMax(SkFixedRoundToInt(fg), a);
304 int b = SkClampMax(SkFixedRoundToInt(fb), a);
305
306 *dst->getAddr32(x,y) = SkPackARGB32(a, r, g, b);
307 }
308 }
309}
310
311static void downScaleVert(const SkBitmap *src, SkBitmap *dst, float scale, SkBitmapFilter *filter) {
312 SkFixed *sums = SkNEW_ARRAY(SkFixed, dst->width() * dst->height() * 4);
313 SkFixed *weights = SkNEW_ARRAY(SkFixed, dst->width() * dst->height());
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000314
humper@google.comb0889472013-07-09 21:37:14 +0000315 SkAutoTDeleteArray<SkFixed> ada1(sums);
316 SkAutoTDeleteArray<SkFixed> ada2(weights);
317
318 memset(sums, 0, dst->width() * dst->height() * sizeof(SkFixed) * 4);
319 memset(weights, 0, dst->width() * dst->height() * sizeof(SkFixed));
320
321 for (int y = 0 ; y < src->height() ; y++) {
322 for (int x = 0 ; x < src->width() ; x++) {
323 // splat each source pixel into the destination image
324 float dy = (y + 0.5f) * scale;
325 int y0 = SkClampMax(int(ceil(dy-filter->width() + 0.5f)), dst->height()-1);
326 int y1 = SkClampMax(int(floor(dy+filter->width() + 0.5f)), dst->height()-1);
327
328 SkPMColor c = *src->getAddr32(x,y);
329
330 for (int dst_y = y0 ; dst_y <= y1 ; dst_y++) {
331 SkFixed weight = filter->lookup(dy - dst_y);
332 sums[4*(dst_y*dst->width() + x) + 0] += weight*SkGetPackedR32(c);
333 sums[4*(dst_y*dst->width() + x) + 1] += weight*SkGetPackedG32(c);
334 sums[4*(dst_y*dst->width() + x) + 2] += weight*SkGetPackedB32(c);
335 sums[4*(dst_y*dst->width() + x) + 3] += weight*SkGetPackedA32(c);
336 weights[dst_y*dst->width() + x] += weight;
337 }
338 }
339 }
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000340
humper@google.comb0889472013-07-09 21:37:14 +0000341 divideByWeights(sums, weights, dst);
342}
343
344void SkBitmap::scale(SkBitmap *dst) const {
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000345
humper@google.comb0889472013-07-09 21:37:14 +0000346 SkBitmap horiz_temp;
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000347
humper@google.comb0889472013-07-09 21:37:14 +0000348 horiz_temp.setConfig(SkBitmap::kARGB_8888_Config, dst->width(), height());
349 horiz_temp.allocPixels();
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000350
humper@google.comb0889472013-07-09 21:37:14 +0000351 SkBitmapFilter *filter = allocateBitmapFilter();
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000352
humper@google.comb0889472013-07-09 21:37:14 +0000353 float horiz_scale = float(dst->width()) / width();
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000354
humper@google.comb0889472013-07-09 21:37:14 +0000355 if (horiz_scale == 1) {
356 this->copyPixelsTo(horiz_temp.getPixels(), getSize());
357 } else if (horiz_scale > 1) {
358 upScaleHoriz(this, &horiz_temp, horiz_scale, filter);
359 } else if (horiz_scale < 1) {
360 downScaleHoriz(this, &horiz_temp, horiz_scale, filter);
361 }
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000362
humper@google.comb0889472013-07-09 21:37:14 +0000363 float vert_scale = float(dst->height()) / height();
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000364
humper@google.comb0889472013-07-09 21:37:14 +0000365 if (vert_scale == 1) {
366 horiz_temp.copyPixelsTo(dst->getPixels(), dst->getSize());
367 } else if (vert_scale > 1) {
368 upScaleVert(&horiz_temp, dst, vert_scale, filter);
369 } else if (vert_scale < 1) {
370 downScaleVert(&horiz_temp, dst, vert_scale, filter);
371 }
skia.committer@gmail.com9e1ec1a2013-07-10 07:00:58 +0000372
humper@google.comb0889472013-07-09 21:37:14 +0000373 SkDELETE(filter);
374}