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