1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/gfx/skbitmap_operations.h"
10 #include "base/logging.h"
11 #include "skia/ext/refptr.h"
12 #include "third_party/skia/include/core/SkBitmap.h"
13 #include "third_party/skia/include/core/SkCanvas.h"
14 #include "third_party/skia/include/core/SkColorFilter.h"
15 #include "third_party/skia/include/core/SkColorPriv.h"
16 #include "third_party/skia/include/core/SkUnPreMultiply.h"
17 #include "third_party/skia/include/effects/SkBlurImageFilter.h"
18 #include "ui/gfx/insets.h"
19 #include "ui/gfx/point.h"
20 #include "ui/gfx/size.h"
23 SkBitmap
SkBitmapOperations::CreateInvertedBitmap(const SkBitmap
& image
) {
24 DCHECK(image
.colorType() == kPMColor_SkColorType
);
26 SkAutoLockPixels
lock_image(image
);
29 inverted
.allocN32Pixels(image
.width(), image
.height());
30 inverted
.eraseARGB(0, 0, 0, 0);
32 for (int y
= 0; y
< image
.height(); ++y
) {
33 uint32
* image_row
= image
.getAddr32(0, y
);
34 uint32
* dst_row
= inverted
.getAddr32(0, y
);
36 for (int x
= 0; x
< image
.width(); ++x
) {
37 uint32 image_pixel
= image_row
[x
];
38 dst_row
[x
] = (image_pixel
& 0xFF000000) |
39 (0x00FFFFFF - (image_pixel
& 0x00FFFFFF));
47 SkBitmap
SkBitmapOperations::CreateSuperimposedBitmap(const SkBitmap
& first
,
48 const SkBitmap
& second
) {
49 DCHECK(first
.width() == second
.width());
50 DCHECK(first
.height() == second
.height());
51 DCHECK(first
.bytesPerPixel() == second
.bytesPerPixel());
52 DCHECK(first
.colorType() == kPMColor_SkColorType
);
54 SkAutoLockPixels
lock_first(first
);
55 SkAutoLockPixels
lock_second(second
);
57 SkBitmap superimposed
;
58 superimposed
.allocN32Pixels(first
.width(), first
.height());
59 superimposed
.eraseARGB(0, 0, 0, 0);
61 SkCanvas
canvas(superimposed
);
66 rect
.fRight
= SkIntToScalar(first
.width());
67 rect
.fBottom
= SkIntToScalar(first
.height());
69 canvas
.drawBitmapRect(first
, NULL
, rect
);
70 canvas
.drawBitmapRect(second
, NULL
, rect
);
76 SkBitmap
SkBitmapOperations::CreateBlendedBitmap(const SkBitmap
& first
,
77 const SkBitmap
& second
,
79 DCHECK((alpha
>= 0) && (alpha
<= 1));
80 DCHECK(first
.width() == second
.width());
81 DCHECK(first
.height() == second
.height());
82 DCHECK(first
.bytesPerPixel() == second
.bytesPerPixel());
83 DCHECK(first
.colorType() == kPMColor_SkColorType
);
85 // Optimize for case where we won't need to blend anything.
86 static const double alpha_min
= 1.0 / 255;
87 static const double alpha_max
= 254.0 / 255;
88 if (alpha
< alpha_min
)
90 else if (alpha
> alpha_max
)
93 SkAutoLockPixels
lock_first(first
);
94 SkAutoLockPixels
lock_second(second
);
97 blended
.allocN32Pixels(first
.width(), first
.height());
98 blended
.eraseARGB(0, 0, 0, 0);
100 double first_alpha
= 1 - alpha
;
102 for (int y
= 0; y
< first
.height(); ++y
) {
103 uint32
* first_row
= first
.getAddr32(0, y
);
104 uint32
* second_row
= second
.getAddr32(0, y
);
105 uint32
* dst_row
= blended
.getAddr32(0, y
);
107 for (int x
= 0; x
< first
.width(); ++x
) {
108 uint32 first_pixel
= first_row
[x
];
109 uint32 second_pixel
= second_row
[x
];
111 int a
= static_cast<int>((SkColorGetA(first_pixel
) * first_alpha
) +
112 (SkColorGetA(second_pixel
) * alpha
));
113 int r
= static_cast<int>((SkColorGetR(first_pixel
) * first_alpha
) +
114 (SkColorGetR(second_pixel
) * alpha
));
115 int g
= static_cast<int>((SkColorGetG(first_pixel
) * first_alpha
) +
116 (SkColorGetG(second_pixel
) * alpha
));
117 int b
= static_cast<int>((SkColorGetB(first_pixel
) * first_alpha
) +
118 (SkColorGetB(second_pixel
) * alpha
));
120 dst_row
[x
] = SkColorSetARGB(a
, r
, g
, b
);
128 SkBitmap
SkBitmapOperations::CreateMaskedBitmap(const SkBitmap
& rgb
,
129 const SkBitmap
& alpha
) {
130 DCHECK(rgb
.width() == alpha
.width());
131 DCHECK(rgb
.height() == alpha
.height());
132 DCHECK(rgb
.bytesPerPixel() == alpha
.bytesPerPixel());
133 DCHECK(rgb
.colorType() == kPMColor_SkColorType
);
134 DCHECK(alpha
.colorType() == kPMColor_SkColorType
);
137 masked
.allocN32Pixels(rgb
.width(), rgb
.height());
138 masked
.eraseARGB(0, 0, 0, 0);
140 SkAutoLockPixels
lock_rgb(rgb
);
141 SkAutoLockPixels
lock_alpha(alpha
);
142 SkAutoLockPixels
lock_masked(masked
);
144 for (int y
= 0; y
< masked
.height(); ++y
) {
145 uint32
* rgb_row
= rgb
.getAddr32(0, y
);
146 uint32
* alpha_row
= alpha
.getAddr32(0, y
);
147 uint32
* dst_row
= masked
.getAddr32(0, y
);
149 for (int x
= 0; x
< masked
.width(); ++x
) {
150 SkColor rgb_pixel
= SkUnPreMultiply::PMColorToColor(rgb_row
[x
]);
151 SkColor alpha_pixel
= SkUnPreMultiply::PMColorToColor(alpha_row
[x
]);
152 int alpha
= SkAlphaMul(SkColorGetA(rgb_pixel
),
153 SkAlpha255To256(SkColorGetA(alpha_pixel
)));
154 int alpha_256
= SkAlpha255To256(alpha
);
155 dst_row
[x
] = SkColorSetARGB(alpha
,
156 SkAlphaMul(SkColorGetR(rgb_pixel
), alpha_256
),
157 SkAlphaMul(SkColorGetG(rgb_pixel
), alpha_256
),
158 SkAlphaMul(SkColorGetB(rgb_pixel
),
167 SkBitmap
SkBitmapOperations::CreateButtonBackground(SkColor color
,
168 const SkBitmap
& image
,
169 const SkBitmap
& mask
) {
170 DCHECK(image
.colorType() == kPMColor_SkColorType
);
171 DCHECK(mask
.colorType() == kPMColor_SkColorType
);
174 background
.allocN32Pixels(mask
.width(), mask
.height());
176 double bg_a
= SkColorGetA(color
);
177 double bg_r
= SkColorGetR(color
);
178 double bg_g
= SkColorGetG(color
);
179 double bg_b
= SkColorGetB(color
);
181 SkAutoLockPixels
lock_mask(mask
);
182 SkAutoLockPixels
lock_image(image
);
183 SkAutoLockPixels
lock_background(background
);
185 for (int y
= 0; y
< mask
.height(); ++y
) {
186 uint32
* dst_row
= background
.getAddr32(0, y
);
187 uint32
* image_row
= image
.getAddr32(0, y
% image
.height());
188 uint32
* mask_row
= mask
.getAddr32(0, y
);
190 for (int x
= 0; x
< mask
.width(); ++x
) {
191 uint32 image_pixel
= image_row
[x
% image
.width()];
193 double img_a
= SkColorGetA(image_pixel
);
194 double img_r
= SkColorGetR(image_pixel
);
195 double img_g
= SkColorGetG(image_pixel
);
196 double img_b
= SkColorGetB(image_pixel
);
198 double img_alpha
= static_cast<double>(img_a
) / 255.0;
199 double img_inv
= 1 - img_alpha
;
201 double mask_a
= static_cast<double>(SkColorGetA(mask_row
[x
])) / 255.0;
203 dst_row
[x
] = SkColorSetARGB(
204 static_cast<int>(std::min(255.0, bg_a
+ img_a
) * mask_a
),
205 static_cast<int>(((bg_r
* img_inv
) + (img_r
* img_alpha
)) * mask_a
),
206 static_cast<int>(((bg_g
* img_inv
) + (img_g
* img_alpha
)) * mask_a
),
207 static_cast<int>(((bg_b
* img_inv
) + (img_b
* img_alpha
)) * mask_a
));
217 // TODO(viettrungluu): Some things have yet to be optimized at all.
219 // Notes on and conventions used in the following code
222 // - R, G, B, A = obvious; as variables: |r|, |g|, |b|, |a| (see also below)
223 // - H, S, L = obvious; as variables: |h|, |s|, |l| (see also below)
224 // - variables derived from S, L shift parameters: |sdec| and |sinc| for S
225 // increase and decrease factors, |ldec| and |linc| for L (see also below)
227 // To try to optimize HSL shifts, we do several things:
228 // - Avoid unpremultiplying (then processing) then premultiplying. This means
229 // that R, G, B values (and also L, but not H and S) should be treated as
230 // having a range of 0..A (where A is alpha).
231 // - Do things in integer/fixed-point. This avoids costly conversions between
232 // floating-point and integer, though I should study the tradeoff more
233 // carefully (presumably, at some point of processing complexity, converting
234 // and processing using simpler floating-point code will begin to win in
235 // performance). Also to be studied is the speed/type of floating point
236 // conversions; see, e.g., <http://www.stereopsis.com/sree/fpu2006.html>.
238 // Conventions for fixed-point arithmetic
239 // - Each function has a constant denominator (called |den|, which should be a
240 // power of 2), appropriate for the computations done in that function.
241 // - A value |x| is then typically represented by a numerator, named |x_num|,
242 // so that its actual value is |x_num / den| (casting to floating-point
244 // - To obtain |x_num| from |x|, simply multiply by |den|, i.e., |x_num = x *
245 // den| (casting appropriately).
246 // - When necessary, a value |x| may also be represented as a numerator over
247 // the denominator squared (set |den2 = den * den|). In such a case, the
248 // corresponding variable is called |x_num2| (so that its actual value is
250 // - The representation of the product of |x| and |y| is be called |x_y_num| if
251 // |x * y == x_y_num / den|, and |xy_num2| if |x * y == x_y_num2 / den2|. In
252 // the latter case, notice that one can calculate |x_y_num2 = x_num * y_num|.
254 // Routine used to process a line; typically specialized for specific kinds of
255 // HSL shifts (to optimize).
256 typedef void (*LineProcessor
)(const color_utils::HSL
&,
261 enum OperationOnH
{ kOpHNone
= 0, kOpHShift
, kNumHOps
};
262 enum OperationOnS
{ kOpSNone
= 0, kOpSDec
, kOpSInc
, kNumSOps
};
263 enum OperationOnL
{ kOpLNone
= 0, kOpLDec
, kOpLInc
, kNumLOps
};
265 // Epsilon used to judge when shift values are close enough to various critical
266 // values (typically 0.5, which yields a no-op for S and L shifts. 1/256 should
267 // be small enough, but let's play it safe>
268 const double epsilon
= 0.0005;
270 // Line processor: default/universal (i.e., old-school).
271 void LineProcDefault(const color_utils::HSL
& hsl_shift
,
275 for (int x
= 0; x
< width
; x
++) {
276 out
[x
] = SkPreMultiplyColor(color_utils::HSLShift(
277 SkUnPreMultiply::PMColorToColor(in
[x
]), hsl_shift
));
281 // Line processor: no-op (i.e., copy).
282 void LineProcCopy(const color_utils::HSL
& hsl_shift
,
286 DCHECK(hsl_shift
.h
< 0);
287 DCHECK(hsl_shift
.s
< 0 || fabs(hsl_shift
.s
- 0.5) < HSLShift::epsilon
);
288 DCHECK(hsl_shift
.l
< 0 || fabs(hsl_shift
.l
- 0.5) < HSLShift::epsilon
);
289 memcpy(out
, in
, static_cast<size_t>(width
) * sizeof(out
[0]));
292 // Line processor: H no-op, S no-op, L decrease.
293 void LineProcHnopSnopLdec(const color_utils::HSL
& hsl_shift
,
297 const uint32_t den
= 65536;
299 DCHECK(hsl_shift
.h
< 0);
300 DCHECK(hsl_shift
.s
< 0 || fabs(hsl_shift
.s
- 0.5) < HSLShift::epsilon
);
301 DCHECK(hsl_shift
.l
<= 0.5 - HSLShift::epsilon
&& hsl_shift
.l
>= 0);
303 uint32_t ldec_num
= static_cast<uint32_t>(hsl_shift
.l
* 2 * den
);
304 for (int x
= 0; x
< width
; x
++) {
305 uint32_t a
= SkGetPackedA32(in
[x
]);
306 uint32_t r
= SkGetPackedR32(in
[x
]);
307 uint32_t g
= SkGetPackedG32(in
[x
]);
308 uint32_t b
= SkGetPackedB32(in
[x
]);
309 r
= r
* ldec_num
/ den
;
310 g
= g
* ldec_num
/ den
;
311 b
= b
* ldec_num
/ den
;
312 out
[x
] = SkPackARGB32(a
, r
, g
, b
);
316 // Line processor: H no-op, S no-op, L increase.
317 void LineProcHnopSnopLinc(const color_utils::HSL
& hsl_shift
,
321 const uint32_t den
= 65536;
323 DCHECK(hsl_shift
.h
< 0);
324 DCHECK(hsl_shift
.s
< 0 || fabs(hsl_shift
.s
- 0.5) < HSLShift::epsilon
);
325 DCHECK(hsl_shift
.l
>= 0.5 + HSLShift::epsilon
&& hsl_shift
.l
<= 1);
327 uint32_t linc_num
= static_cast<uint32_t>((hsl_shift
.l
- 0.5) * 2 * den
);
328 for (int x
= 0; x
< width
; x
++) {
329 uint32_t a
= SkGetPackedA32(in
[x
]);
330 uint32_t r
= SkGetPackedR32(in
[x
]);
331 uint32_t g
= SkGetPackedG32(in
[x
]);
332 uint32_t b
= SkGetPackedB32(in
[x
]);
333 r
+= (a
- r
) * linc_num
/ den
;
334 g
+= (a
- g
) * linc_num
/ den
;
335 b
+= (a
- b
) * linc_num
/ den
;
336 out
[x
] = SkPackARGB32(a
, r
, g
, b
);
340 // Saturation changes modifications in RGB
342 // (Note that as a further complication, the values we deal in are
343 // premultiplied, so R/G/B values must be in the range 0..A. For mathematical
344 // purposes, one may as well use r=R/A, g=G/A, b=B/A. Without loss of
345 // generality, assume that R/G/B values are in the range 0..1.)
347 // Let Max = max(R,G,B), Min = min(R,G,B), and Med be the median value. Then L =
348 // (Max+Min)/2. If L is to remain constant, Max+Min must also remain constant.
350 // For H to remain constant, first, the (numerical) order of R/G/B (from
351 // smallest to largest) must remain the same. Second, all the ratios
352 // (R-G)/(Max-Min), (R-B)/(Max-Min), (G-B)/(Max-Min) must remain constant (of
353 // course, if Max = Min, then S = 0 and no saturation change is well-defined,
354 // since H is not well-defined).
356 // Let C_max be a colour with value Max, C_min be one with value Min, and C_med
357 // the remaining colour. Increasing saturation (to the maximum) is accomplished
358 // by increasing the value of C_max while simultaneously decreasing C_min and
359 // changing C_med so that the ratios are maintained; for the latter, it suffices
360 // to keep (C_med-C_min)/(C_max-C_min) constant (and equal to
361 // (Med-Min)/(Max-Min)).
363 // Line processor: H no-op, S decrease, L no-op.
364 void LineProcHnopSdecLnop(const color_utils::HSL
& hsl_shift
,
368 DCHECK(hsl_shift
.h
< 0);
369 DCHECK(hsl_shift
.s
>= 0 && hsl_shift
.s
<= 0.5 - HSLShift::epsilon
);
370 DCHECK(hsl_shift
.l
< 0 || fabs(hsl_shift
.l
- 0.5) < HSLShift::epsilon
);
372 const int32_t denom
= 65536;
373 int32_t s_numer
= static_cast<int32_t>(hsl_shift
.s
* 2 * denom
);
374 for (int x
= 0; x
< width
; x
++) {
375 int32_t a
= static_cast<int32_t>(SkGetPackedA32(in
[x
]));
376 int32_t r
= static_cast<int32_t>(SkGetPackedR32(in
[x
]));
377 int32_t g
= static_cast<int32_t>(SkGetPackedG32(in
[x
]));
378 int32_t b
= static_cast<int32_t>(SkGetPackedB32(in
[x
]));
381 if (r
> g
) { // This uses 3 compares rather than 4.
382 vmax
= std::max(r
, b
);
383 vmin
= std::min(g
, b
);
385 vmax
= std::max(g
, b
);
386 vmin
= std::min(r
, b
);
389 // Use denom * L to avoid rounding.
390 int32_t denom_l
= (vmax
+ vmin
) * (denom
/ 2);
391 int32_t s_numer_l
= (vmax
+ vmin
) * s_numer
/ 2;
393 r
= (denom_l
+ r
* s_numer
- s_numer_l
) / denom
;
394 g
= (denom_l
+ g
* s_numer
- s_numer_l
) / denom
;
395 b
= (denom_l
+ b
* s_numer
- s_numer_l
) / denom
;
396 out
[x
] = SkPackARGB32(a
, r
, g
, b
);
400 // Line processor: H no-op, S decrease, L decrease.
401 void LineProcHnopSdecLdec(const color_utils::HSL
& hsl_shift
,
405 DCHECK(hsl_shift
.h
< 0);
406 DCHECK(hsl_shift
.s
>= 0 && hsl_shift
.s
<= 0.5 - HSLShift::epsilon
);
407 DCHECK(hsl_shift
.l
>= 0 && hsl_shift
.l
<= 0.5 - HSLShift::epsilon
);
409 // Can't be too big since we need room for denom*denom and a bit for sign.
410 const int32_t denom
= 1024;
411 int32_t l_numer
= static_cast<int32_t>(hsl_shift
.l
* 2 * denom
);
412 int32_t s_numer
= static_cast<int32_t>(hsl_shift
.s
* 2 * denom
);
413 for (int x
= 0; x
< width
; x
++) {
414 int32_t a
= static_cast<int32_t>(SkGetPackedA32(in
[x
]));
415 int32_t r
= static_cast<int32_t>(SkGetPackedR32(in
[x
]));
416 int32_t g
= static_cast<int32_t>(SkGetPackedG32(in
[x
]));
417 int32_t b
= static_cast<int32_t>(SkGetPackedB32(in
[x
]));
420 if (r
> g
) { // This uses 3 compares rather than 4.
421 vmax
= std::max(r
, b
);
422 vmin
= std::min(g
, b
);
424 vmax
= std::max(g
, b
);
425 vmin
= std::min(r
, b
);
428 // Use denom * L to avoid rounding.
429 int32_t denom_l
= (vmax
+ vmin
) * (denom
/ 2);
430 int32_t s_numer_l
= (vmax
+ vmin
) * s_numer
/ 2;
432 r
= (denom_l
+ r
* s_numer
- s_numer_l
) * l_numer
/ (denom
* denom
);
433 g
= (denom_l
+ g
* s_numer
- s_numer_l
) * l_numer
/ (denom
* denom
);
434 b
= (denom_l
+ b
* s_numer
- s_numer_l
) * l_numer
/ (denom
* denom
);
435 out
[x
] = SkPackARGB32(a
, r
, g
, b
);
439 // Line processor: H no-op, S decrease, L increase.
440 void LineProcHnopSdecLinc(const color_utils::HSL
& hsl_shift
,
444 DCHECK(hsl_shift
.h
< 0);
445 DCHECK(hsl_shift
.s
>= 0 && hsl_shift
.s
<= 0.5 - HSLShift::epsilon
);
446 DCHECK(hsl_shift
.l
>= 0.5 + HSLShift::epsilon
&& hsl_shift
.l
<= 1);
448 // Can't be too big since we need room for denom*denom and a bit for sign.
449 const int32_t denom
= 1024;
450 int32_t l_numer
= static_cast<int32_t>((hsl_shift
.l
- 0.5) * 2 * denom
);
451 int32_t s_numer
= static_cast<int32_t>(hsl_shift
.s
* 2 * denom
);
452 for (int x
= 0; x
< width
; x
++) {
453 int32_t a
= static_cast<int32_t>(SkGetPackedA32(in
[x
]));
454 int32_t r
= static_cast<int32_t>(SkGetPackedR32(in
[x
]));
455 int32_t g
= static_cast<int32_t>(SkGetPackedG32(in
[x
]));
456 int32_t b
= static_cast<int32_t>(SkGetPackedB32(in
[x
]));
459 if (r
> g
) { // This uses 3 compares rather than 4.
460 vmax
= std::max(r
, b
);
461 vmin
= std::min(g
, b
);
463 vmax
= std::max(g
, b
);
464 vmin
= std::min(r
, b
);
467 // Use denom * L to avoid rounding.
468 int32_t denom_l
= (vmax
+ vmin
) * (denom
/ 2);
469 int32_t s_numer_l
= (vmax
+ vmin
) * s_numer
/ 2;
471 r
= denom_l
+ r
* s_numer
- s_numer_l
;
472 g
= denom_l
+ g
* s_numer
- s_numer_l
;
473 b
= denom_l
+ b
* s_numer
- s_numer_l
;
475 r
= (r
* denom
+ (a
* denom
- r
) * l_numer
) / (denom
* denom
);
476 g
= (g
* denom
+ (a
* denom
- g
) * l_numer
) / (denom
* denom
);
477 b
= (b
* denom
+ (a
* denom
- b
) * l_numer
) / (denom
* denom
);
478 out
[x
] = SkPackARGB32(a
, r
, g
, b
);
482 const LineProcessor kLineProcessors
[kNumHOps
][kNumSOps
][kNumLOps
] = {
485 LineProcCopy
, // L: kOpLNone
486 LineProcHnopSnopLdec
, // L: kOpLDec
487 LineProcHnopSnopLinc
// L: kOpLInc
490 LineProcHnopSdecLnop
, // L: kOpLNone
491 LineProcHnopSdecLdec
, // L: kOpLDec
492 LineProcHnopSdecLinc
// L: kOpLInc
495 LineProcDefault
, // L: kOpLNone
496 LineProcDefault
, // L: kOpLDec
497 LineProcDefault
// L: kOpLInc
502 LineProcDefault
, // L: kOpLNone
503 LineProcDefault
, // L: kOpLDec
504 LineProcDefault
// L: kOpLInc
507 LineProcDefault
, // L: kOpLNone
508 LineProcDefault
, // L: kOpLDec
509 LineProcDefault
// L: kOpLInc
512 LineProcDefault
, // L: kOpLNone
513 LineProcDefault
, // L: kOpLDec
514 LineProcDefault
// L: kOpLInc
519 } // namespace HSLShift
523 SkBitmap
SkBitmapOperations::CreateHSLShiftedBitmap(
524 const SkBitmap
& bitmap
,
525 const color_utils::HSL
& hsl_shift
) {
527 HSLShift::OperationOnH H_op
= HSLShift::kOpHNone
;
528 HSLShift::OperationOnS S_op
= HSLShift::kOpSNone
;
529 HSLShift::OperationOnL L_op
= HSLShift::kOpLNone
;
531 if (hsl_shift
.h
>= 0 && hsl_shift
.h
<= 1)
532 H_op
= HSLShift::kOpHShift
;
534 // Saturation shift: 0 -> fully desaturate, 0.5 -> NOP, 1 -> fully saturate.
535 if (hsl_shift
.s
>= 0 && hsl_shift
.s
<= (0.5 - HSLShift::epsilon
))
536 S_op
= HSLShift::kOpSDec
;
537 else if (hsl_shift
.s
>= (0.5 + HSLShift::epsilon
))
538 S_op
= HSLShift::kOpSInc
;
540 // Lightness shift: 0 -> black, 0.5 -> NOP, 1 -> white.
541 if (hsl_shift
.l
>= 0 && hsl_shift
.l
<= (0.5 - HSLShift::epsilon
))
542 L_op
= HSLShift::kOpLDec
;
543 else if (hsl_shift
.l
>= (0.5 + HSLShift::epsilon
))
544 L_op
= HSLShift::kOpLInc
;
546 HSLShift::LineProcessor line_proc
=
547 HSLShift::kLineProcessors
[H_op
][S_op
][L_op
];
549 DCHECK(bitmap
.empty() == false);
550 DCHECK(bitmap
.colorType() == kPMColor_SkColorType
);
553 shifted
.allocN32Pixels(bitmap
.width(), bitmap
.height());
554 shifted
.eraseARGB(0, 0, 0, 0);
556 SkAutoLockPixels
lock_bitmap(bitmap
);
557 SkAutoLockPixels
lock_shifted(shifted
);
559 // Loop through the pixels of the original bitmap.
560 for (int y
= 0; y
< bitmap
.height(); ++y
) {
561 SkPMColor
* pixels
= bitmap
.getAddr32(0, y
);
562 SkPMColor
* tinted_pixels
= shifted
.getAddr32(0, y
);
564 (*line_proc
)(hsl_shift
, pixels
, tinted_pixels
, bitmap
.width());
571 SkBitmap
SkBitmapOperations::CreateTiledBitmap(const SkBitmap
& source
,
572 int src_x
, int src_y
,
573 int dst_w
, int dst_h
) {
574 DCHECK(source
.colorType() == kPMColor_SkColorType
);
577 cropped
.allocN32Pixels(dst_w
, dst_h
);
578 cropped
.eraseARGB(0, 0, 0, 0);
580 SkAutoLockPixels
lock_source(source
);
581 SkAutoLockPixels
lock_cropped(cropped
);
583 // Loop through the pixels of the original bitmap.
584 for (int y
= 0; y
< dst_h
; ++y
) {
585 int y_pix
= (src_y
+ y
) % source
.height();
587 y_pix
+= source
.height();
589 uint32
* source_row
= source
.getAddr32(0, y_pix
);
590 uint32
* dst_row
= cropped
.getAddr32(0, y
);
592 for (int x
= 0; x
< dst_w
; ++x
) {
593 int x_pix
= (src_x
+ x
) % source
.width();
595 x_pix
+= source
.width();
597 dst_row
[x
] = source_row
[x_pix
];
605 SkBitmap
SkBitmapOperations::DownsampleByTwoUntilSize(const SkBitmap
& bitmap
,
606 int min_w
, int min_h
) {
607 if ((bitmap
.width() <= min_w
) || (bitmap
.height() <= min_h
) ||
608 (min_w
< 0) || (min_h
< 0))
611 // Since bitmaps are refcounted, this copy will be fast.
612 SkBitmap current
= bitmap
;
613 while ((current
.width() >= min_w
* 2) && (current
.height() >= min_h
* 2) &&
614 (current
.width() > 1) && (current
.height() > 1))
615 current
= DownsampleByTwo(current
);
620 SkBitmap
SkBitmapOperations::DownsampleByTwo(const SkBitmap
& bitmap
) {
621 // Handle the nop case.
622 if ((bitmap
.width() <= 1) || (bitmap
.height() <= 1))
626 result
.allocN32Pixels((bitmap
.width() + 1) / 2, (bitmap
.height() + 1) / 2);
628 SkAutoLockPixels
lock(bitmap
);
630 const int resultLastX
= result
.width() - 1;
631 const int srcLastX
= bitmap
.width() - 1;
633 for (int dest_y
= 0; dest_y
< result
.height(); ++dest_y
) {
634 const int src_y
= dest_y
<< 1;
635 const SkPMColor
* SK_RESTRICT cur_src0
= bitmap
.getAddr32(0, src_y
);
636 const SkPMColor
* SK_RESTRICT cur_src1
= cur_src0
;
637 if (src_y
+ 1 < bitmap
.height())
638 cur_src1
= bitmap
.getAddr32(0, src_y
+ 1);
640 SkPMColor
* SK_RESTRICT cur_dst
= result
.getAddr32(0, dest_y
);
642 for (int dest_x
= 0; dest_x
<= resultLastX
; ++dest_x
) {
643 // This code is based on downsampleby2_proc32 in SkBitmap.cpp. It is very
644 // clever in that it does two channels at once: alpha and green ("ag")
645 // and red and blue ("rb"). Each channel gets averaged across 4 pixels
646 // to get the result.
647 int bump_x
= (dest_x
<< 1) < srcLastX
;
648 SkPMColor tmp
, ag
, rb
;
650 // Top left pixel of the 2x2 block.
652 ag
= (tmp
>> 8) & 0xFF00FF;
655 // Top right pixel of the 2x2 block.
656 tmp
= cur_src0
[bump_x
];
657 ag
+= (tmp
>> 8) & 0xFF00FF;
658 rb
+= tmp
& 0xFF00FF;
660 // Bottom left pixel of the 2x2 block.
662 ag
+= (tmp
>> 8) & 0xFF00FF;
663 rb
+= tmp
& 0xFF00FF;
665 // Bottom right pixel of the 2x2 block.
666 tmp
= cur_src1
[bump_x
];
667 ag
+= (tmp
>> 8) & 0xFF00FF;
668 rb
+= tmp
& 0xFF00FF;
670 // Put the channels back together, dividing each by 4 to get the average.
671 // |ag| has the alpha and green channels shifted right by 8 bits from
672 // there they should end up, so shifting left by 6 gives them in the
673 // correct position divided by 4.
674 *cur_dst
++ = ((rb
>> 2) & 0xFF00FF) | ((ag
<< 6) & 0xFF00FF00);
685 SkBitmap
SkBitmapOperations::UnPreMultiply(const SkBitmap
& bitmap
) {
688 if (bitmap
.isOpaque())
691 SkImageInfo info
= bitmap
.info();
692 info
.fAlphaType
= kOpaque_SkAlphaType
;
693 SkBitmap opaque_bitmap
;
694 opaque_bitmap
.allocPixels(info
);
697 SkAutoLockPixels
bitmap_lock(bitmap
);
698 SkAutoLockPixels
opaque_bitmap_lock(opaque_bitmap
);
699 for (int y
= 0; y
< opaque_bitmap
.height(); y
++) {
700 for (int x
= 0; x
< opaque_bitmap
.width(); x
++) {
701 uint32 src_pixel
= *bitmap
.getAddr32(x
, y
);
702 uint32
* dst_pixel
= opaque_bitmap
.getAddr32(x
, y
);
703 SkColor unmultiplied
= SkUnPreMultiply::PMColorToColor(src_pixel
);
704 *dst_pixel
= unmultiplied
;
709 return opaque_bitmap
;
713 SkBitmap
SkBitmapOperations::CreateTransposedBitmap(const SkBitmap
& image
) {
714 DCHECK(image
.colorType() == kPMColor_SkColorType
);
717 transposed
.allocN32Pixels(image
.height(), image
.width());
719 SkAutoLockPixels
lock_image(image
);
720 SkAutoLockPixels
lock_transposed(transposed
);
722 for (int y
= 0; y
< image
.height(); ++y
) {
723 uint32
* image_row
= image
.getAddr32(0, y
);
724 for (int x
= 0; x
< image
.width(); ++x
) {
725 uint32
* dst
= transposed
.getAddr32(y
, x
);
734 SkBitmap
SkBitmapOperations::CreateColorMask(const SkBitmap
& bitmap
,
736 DCHECK(bitmap
.colorType() == kPMColor_SkColorType
);
739 color_mask
.allocN32Pixels(bitmap
.width(), bitmap
.height());
740 color_mask
.eraseARGB(0, 0, 0, 0);
742 SkCanvas
canvas(color_mask
);
744 skia::RefPtr
<SkColorFilter
> color_filter
= skia::AdoptRef(
745 SkColorFilter::CreateModeFilter(c
, SkXfermode::kSrcIn_Mode
));
747 paint
.setColorFilter(color_filter
.get());
748 canvas
.drawBitmap(bitmap
, SkIntToScalar(0), SkIntToScalar(0), &paint
);
753 SkBitmap
SkBitmapOperations::CreateDropShadow(
754 const SkBitmap
& bitmap
,
755 const gfx::ShadowValues
& shadows
) {
756 DCHECK(bitmap
.colorType() == kPMColor_SkColorType
);
758 // Shadow margin insets are negative values because they grow outside.
759 // Negate them here as grow direction is not important and only pixel value
760 // is of interest here.
761 gfx::Insets shadow_margin
= -gfx::ShadowValue::GetMargin(shadows
);
763 SkBitmap image_with_shadow
;
764 image_with_shadow
.allocN32Pixels(bitmap
.width() + shadow_margin
.width(),
765 bitmap
.height() + shadow_margin
.height());
766 image_with_shadow
.eraseARGB(0, 0, 0, 0);
768 SkCanvas
canvas(image_with_shadow
);
769 canvas
.translate(SkIntToScalar(shadow_margin
.left()),
770 SkIntToScalar(shadow_margin
.top()));
773 for (size_t i
= 0; i
< shadows
.size(); ++i
) {
774 const gfx::ShadowValue
& shadow
= shadows
[i
];
775 SkBitmap shadow_image
= SkBitmapOperations::CreateColorMask(bitmap
,
778 skia::RefPtr
<SkBlurImageFilter
> filter
=
779 skia::AdoptRef(SkBlurImageFilter::Create(
780 SkDoubleToScalar(shadow
.blur()), SkDoubleToScalar(shadow
.blur())));
781 paint
.setImageFilter(filter
.get());
783 canvas
.saveLayer(0, &paint
);
784 canvas
.drawBitmap(shadow_image
,
785 SkIntToScalar(shadow
.x()),
786 SkIntToScalar(shadow
.y()));
790 canvas
.drawBitmap(bitmap
, SkIntToScalar(0), SkIntToScalar(0));
791 return image_with_shadow
;
795 SkBitmap
SkBitmapOperations::Rotate(const SkBitmap
& source
,
796 RotationAmount rotation
) {
798 SkScalar angle
= SkFloatToScalar(0.0f
);
802 angle
= SkFloatToScalar(90.0f
);
804 SkBitmap::kARGB_8888_Config
, source
.height(), source
.width());
806 case ROTATION_180_CW
:
807 angle
= SkFloatToScalar(180.0f
);
809 SkBitmap::kARGB_8888_Config
, source
.width(), source
.height());
811 case ROTATION_270_CW
:
812 angle
= SkFloatToScalar(270.0f
);
814 SkBitmap::kARGB_8888_Config
, source
.height(), source
.width());
817 result
.allocPixels();
818 SkCanvas
canvas(result
);
819 canvas
.clear(SkColorSetARGB(0, 0, 0, 0));
821 canvas
.translate(SkFloatToScalar(result
.width() * 0.5f
),
822 SkFloatToScalar(result
.height() * 0.5f
));
823 canvas
.rotate(angle
);
824 canvas
.translate(-SkFloatToScalar(source
.width() * 0.5f
),
825 -SkFloatToScalar(source
.height() * 0.5f
));
826 canvas
.drawBitmap(source
, 0, 0);