SkBitmapFilter.cpp revision b088947f27496a9b9dc48a7cfb170f9d59589825
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, 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); 32 33 SkFixed weight = 0; 34 SkFixed fr = 0, fg = 0, fb = 0, fa = 0; 35 36 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); 40 41 for (int src_y = y0; src_y <= y1; src_y++) { 42 SkFixed yweight = s.getBitmapFilter()->lookup((srcPt.fY - src_y)); 43 44 for (int src_x = x0; src_x <= x1 ; src_x++) { 45 SkFixed xweight = s.getBitmapFilter()->lookup((srcPt.fX - src_x)); 46 47 SkFixed combined_weight = SkFixedMul(xweight, yweight); 48 49 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; 78 79 SkPoint srcPt; 80 81 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++; 132 } 133} 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 } 151 152 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 } 179 180 if (fInvType & (SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask)) { 181 fBitmapFilter = allocateBitmapFilter(); 182 } 183 184 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); 216 217 SkFixed total_weight = 0; 218 SkFixed fr = 0, fg = 0, fb = 0, fa = 0; 219 220 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()); 247 248 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)); 253 254 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); 260 261 SkPMColor c = *src->getAddr32(x,y); 262 263 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 } 273 274 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); 283 284 SkFixed total_weight = 0; 285 SkFixed fr = 0, fg = 0, fb = 0, fa = 0; 286 287 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()); 314 315 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 } 340 341 divideByWeights(sums, weights, dst); 342} 343 344void SkBitmap::scale(SkBitmap *dst) const { 345 346 SkBitmap horiz_temp; 347 348 horiz_temp.setConfig(SkBitmap::kARGB_8888_Config, dst->width(), height()); 349 horiz_temp.allocPixels(); 350 351 SkBitmapFilter *filter = allocateBitmapFilter(); 352 353 float horiz_scale = float(dst->width()) / width(); 354 355 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 } 362 363 float vert_scale = float(dst->height()) / height(); 364 365 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 } 372 373 SkDELETE(filter); 374} 375