blob: 2cc053fc6436f9382223a4a42a56e479c2480e26 [file] [log] [blame]
[email protected]125b62002012-03-19 14:30:401// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]cab34d6a2009-09-24 01:14:522// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]08397d52011-02-05 01:53:385#include "ui/gfx/skbitmap_operations.h"
[email protected]cab34d6a2009-09-24 01:14:526
[email protected]0fb499a2009-11-10 21:03:337#include <algorithm>
[email protected]6e25ce82010-04-26 17:12:408#include <string.h>
[email protected]0fb499a2009-11-10 21:03:339
[email protected]cab34d6a2009-09-24 01:14:5210#include "base/logging.h"
11#include "third_party/skia/include/core/SkBitmap.h"
[email protected]0fb499a2009-11-10 21:03:3312#include "third_party/skia/include/core/SkCanvas.h"
[email protected]6cd13aa2012-03-23 02:27:1613#include "third_party/skia/include/core/SkColorFilter.h"
[email protected]cab34d6a2009-09-24 01:14:5214#include "third_party/skia/include/core/SkColorPriv.h"
15#include "third_party/skia/include/core/SkUnPreMultiply.h"
[email protected]6cd13aa2012-03-23 02:27:1616#include "third_party/skia/include/effects/SkBlurImageFilter.h"
[email protected]58e068f2012-05-26 00:29:1617#include "ui/gfx/insets.h"
[email protected]6cd13aa2012-03-23 02:27:1618#include "ui/gfx/point.h"
19#include "ui/gfx/size.h"
[email protected]cab34d6a2009-09-24 01:14:5220
21// static
[email protected]0fb499a2009-11-10 21:03:3322SkBitmap SkBitmapOperations::CreateInvertedBitmap(const SkBitmap& image) {
23 DCHECK(image.config() == SkBitmap::kARGB_8888_Config);
24
25 SkAutoLockPixels lock_image(image);
26
27 SkBitmap inverted;
28 inverted.setConfig(SkBitmap::kARGB_8888_Config, image.width(), image.height(),
29 0);
30 inverted.allocPixels();
31 inverted.eraseARGB(0, 0, 0, 0);
32
33 for (int y = 0; y < image.height(); ++y) {
34 uint32* image_row = image.getAddr32(0, y);
35 uint32* dst_row = inverted.getAddr32(0, y);
36
37 for (int x = 0; x < image.width(); ++x) {
38 uint32 image_pixel = image_row[x];
39 dst_row[x] = (image_pixel & 0xFF000000) |
40 (0x00FFFFFF - (image_pixel & 0x00FFFFFF));
41 }
42 }
43
44 return inverted;
45}
46
47// static
48SkBitmap SkBitmapOperations::CreateSuperimposedBitmap(const SkBitmap& first,
49 const SkBitmap& second) {
50 DCHECK(first.width() == second.width());
51 DCHECK(first.height() == second.height());
52 DCHECK(first.bytesPerPixel() == second.bytesPerPixel());
53 DCHECK(first.config() == SkBitmap::kARGB_8888_Config);
54
55 SkAutoLockPixels lock_first(first);
56 SkAutoLockPixels lock_second(second);
57
58 SkBitmap superimposed;
59 superimposed.setConfig(SkBitmap::kARGB_8888_Config,
60 first.width(), first.height());
61 superimposed.allocPixels();
62 superimposed.eraseARGB(0, 0, 0, 0);
63
64 SkCanvas canvas(superimposed);
65
66 SkRect rect;
67 rect.fLeft = 0;
68 rect.fTop = 0;
69 rect.fRight = SkIntToScalar(first.width());
70 rect.fBottom = SkIntToScalar(first.height());
71
72 canvas.drawBitmapRect(first, NULL, rect);
73 canvas.drawBitmapRect(second, NULL, rect);
74
75 return superimposed;
76}
77
78// static
[email protected]cab34d6a2009-09-24 01:14:5279SkBitmap SkBitmapOperations::CreateBlendedBitmap(const SkBitmap& first,
80 const SkBitmap& second,
81 double alpha) {
82 DCHECK((alpha >= 0) && (alpha <= 1));
83 DCHECK(first.width() == second.width());
84 DCHECK(first.height() == second.height());
85 DCHECK(first.bytesPerPixel() == second.bytesPerPixel());
86 DCHECK(first.config() == SkBitmap::kARGB_8888_Config);
87
88 // Optimize for case where we won't need to blend anything.
89 static const double alpha_min = 1.0 / 255;
90 static const double alpha_max = 254.0 / 255;
91 if (alpha < alpha_min)
92 return first;
93 else if (alpha > alpha_max)
94 return second;
95
96 SkAutoLockPixels lock_first(first);
97 SkAutoLockPixels lock_second(second);
98
99 SkBitmap blended;
100 blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(), first.height(),
101 0);
102 blended.allocPixels();
103 blended.eraseARGB(0, 0, 0, 0);
104
105 double first_alpha = 1 - alpha;
106
107 for (int y = 0; y < first.height(); ++y) {
108 uint32* first_row = first.getAddr32(0, y);
109 uint32* second_row = second.getAddr32(0, y);
110 uint32* dst_row = blended.getAddr32(0, y);
111
112 for (int x = 0; x < first.width(); ++x) {
113 uint32 first_pixel = first_row[x];
114 uint32 second_pixel = second_row[x];
115
116 int a = static_cast<int>((SkColorGetA(first_pixel) * first_alpha) +
117 (SkColorGetA(second_pixel) * alpha));
118 int r = static_cast<int>((SkColorGetR(first_pixel) * first_alpha) +
119 (SkColorGetR(second_pixel) * alpha));
120 int g = static_cast<int>((SkColorGetG(first_pixel) * first_alpha) +
121 (SkColorGetG(second_pixel) * alpha));
122 int b = static_cast<int>((SkColorGetB(first_pixel) * first_alpha) +
123 (SkColorGetB(second_pixel) * alpha));
124
125 dst_row[x] = SkColorSetARGB(a, r, g, b);
126 }
127 }
128
129 return blended;
130}
131
132// static
133SkBitmap SkBitmapOperations::CreateMaskedBitmap(const SkBitmap& rgb,
134 const SkBitmap& alpha) {
135 DCHECK(rgb.width() == alpha.width());
136 DCHECK(rgb.height() == alpha.height());
137 DCHECK(rgb.bytesPerPixel() == alpha.bytesPerPixel());
138 DCHECK(rgb.config() == SkBitmap::kARGB_8888_Config);
139 DCHECK(alpha.config() == SkBitmap::kARGB_8888_Config);
140
141 SkBitmap masked;
142 masked.setConfig(SkBitmap::kARGB_8888_Config, rgb.width(), rgb.height(), 0);
143 masked.allocPixels();
144 masked.eraseARGB(0, 0, 0, 0);
145
146 SkAutoLockPixels lock_rgb(rgb);
147 SkAutoLockPixels lock_alpha(alpha);
148 SkAutoLockPixels lock_masked(masked);
149
150 for (int y = 0; y < masked.height(); ++y) {
151 uint32* rgb_row = rgb.getAddr32(0, y);
152 uint32* alpha_row = alpha.getAddr32(0, y);
153 uint32* dst_row = masked.getAddr32(0, y);
154
155 for (int x = 0; x < masked.width(); ++x) {
156 SkColor rgb_pixel = SkUnPreMultiply::PMColorToColor(rgb_row[x]);
[email protected]97e159012012-01-25 19:48:38157 SkColor alpha_pixel = SkUnPreMultiply::PMColorToColor(alpha_row[x]);
158 int alpha = SkAlphaMul(SkColorGetA(rgb_pixel), SkColorGetA(alpha_pixel));
[email protected]cab34d6a2009-09-24 01:14:52159 dst_row[x] = SkColorSetARGB(alpha,
160 SkAlphaMul(SkColorGetR(rgb_pixel), alpha),
161 SkAlphaMul(SkColorGetG(rgb_pixel), alpha),
162 SkAlphaMul(SkColorGetB(rgb_pixel), alpha));
163 }
164 }
165
166 return masked;
167}
168
169// static
170SkBitmap SkBitmapOperations::CreateButtonBackground(SkColor color,
171 const SkBitmap& image,
172 const SkBitmap& mask) {
173 DCHECK(image.config() == SkBitmap::kARGB_8888_Config);
174 DCHECK(mask.config() == SkBitmap::kARGB_8888_Config);
175
176 SkBitmap background;
177 background.setConfig(
178 SkBitmap::kARGB_8888_Config, mask.width(), mask.height(), 0);
179 background.allocPixels();
180
181 double bg_a = SkColorGetA(color);
182 double bg_r = SkColorGetR(color);
183 double bg_g = SkColorGetG(color);
184 double bg_b = SkColorGetB(color);
185
186 SkAutoLockPixels lock_mask(mask);
187 SkAutoLockPixels lock_image(image);
188 SkAutoLockPixels lock_background(background);
189
190 for (int y = 0; y < mask.height(); ++y) {
191 uint32* dst_row = background.getAddr32(0, y);
192 uint32* image_row = image.getAddr32(0, y % image.height());
193 uint32* mask_row = mask.getAddr32(0, y);
194
195 for (int x = 0; x < mask.width(); ++x) {
196 uint32 image_pixel = image_row[x % image.width()];
197
198 double img_a = SkColorGetA(image_pixel);
199 double img_r = SkColorGetR(image_pixel);
200 double img_g = SkColorGetG(image_pixel);
201 double img_b = SkColorGetB(image_pixel);
202
203 double img_alpha = static_cast<double>(img_a) / 255.0;
204 double img_inv = 1 - img_alpha;
205
206 double mask_a = static_cast<double>(SkColorGetA(mask_row[x])) / 255.0;
207
208 dst_row[x] = SkColorSetARGB(
209 static_cast<int>(std::min(255.0, bg_a + img_a) * mask_a),
210 static_cast<int>(((bg_r * img_inv) + (img_r * img_alpha)) * mask_a),
211 static_cast<int>(((bg_g * img_inv) + (img_g * img_alpha)) * mask_a),
212 static_cast<int>(((bg_b * img_inv) + (img_b * img_alpha)) * mask_a));
213 }
214 }
215
216 return background;
217}
218
[email protected]6e25ce82010-04-26 17:12:40219namespace {
220namespace HSLShift {
221
222// TODO(viettrungluu): Some things have yet to be optimized at all.
223
224// Notes on and conventions used in the following code
225//
226// Conventions:
227// - R, G, B, A = obvious; as variables: |r|, |g|, |b|, |a| (see also below)
228// - H, S, L = obvious; as variables: |h|, |s|, |l| (see also below)
229// - variables derived from S, L shift parameters: |sdec| and |sinc| for S
230// increase and decrease factors, |ldec| and |linc| for L (see also below)
231//
232// To try to optimize HSL shifts, we do several things:
233// - Avoid unpremultiplying (then processing) then premultiplying. This means
234// that R, G, B values (and also L, but not H and S) should be treated as
235// having a range of 0..A (where A is alpha).
236// - Do things in integer/fixed-point. This avoids costly conversions between
237// floating-point and integer, though I should study the tradeoff more
238// carefully (presumably, at some point of processing complexity, converting
239// and processing using simpler floating-point code will begin to win in
240// performance). Also to be studied is the speed/type of floating point
241// conversions; see, e.g., <http://www.stereopsis.com/sree/fpu2006.html>.
242//
243// Conventions for fixed-point arithmetic
244// - Each function has a constant denominator (called |den|, which should be a
245// power of 2), appropriate for the computations done in that function.
246// - A value |x| is then typically represented by a numerator, named |x_num|,
247// so that its actual value is |x_num / den| (casting to floating-point
248// before division).
249// - To obtain |x_num| from |x|, simply multiply by |den|, i.e., |x_num = x *
250// den| (casting appropriately).
251// - When necessary, a value |x| may also be represented as a numerator over
252// the denominator squared (set |den2 = den * den|). In such a case, the
253// corresponding variable is called |x_num2| (so that its actual value is
254// |x_num^2 / den2|.
255// - The representation of the product of |x| and |y| is be called |x_y_num| if
256// |x * y == x_y_num / den|, and |xy_num2| if |x * y == x_y_num2 / den2|. In
257// the latter case, notice that one can calculate |x_y_num2 = x_num * y_num|.
258
259// Routine used to process a line; typically specialized for specific kinds of
260// HSL shifts (to optimize).
[email protected]80b3f7c2011-06-22 02:29:21261typedef void (*LineProcessor)(const color_utils::HSL&,
[email protected]6e25ce82010-04-26 17:12:40262 const SkPMColor*,
263 SkPMColor*,
264 int width);
265
266enum OperationOnH { kOpHNone = 0, kOpHShift, kNumHOps };
267enum OperationOnS { kOpSNone = 0, kOpSDec, kOpSInc, kNumSOps };
268enum OperationOnL { kOpLNone = 0, kOpLDec, kOpLInc, kNumLOps };
269
270// Epsilon used to judge when shift values are close enough to various critical
271// values (typically 0.5, which yields a no-op for S and L shifts. 1/256 should
272// be small enough, but let's play it safe>
273const double epsilon = 0.0005;
274
275// Line processor: default/universal (i.e., old-school).
[email protected]80b3f7c2011-06-22 02:29:21276void LineProcDefault(const color_utils::HSL& hsl_shift,
277 const SkPMColor* in,
278 SkPMColor* out,
279 int width) {
[email protected]6e25ce82010-04-26 17:12:40280 for (int x = 0; x < width; x++) {
281 out[x] = SkPreMultiplyColor(color_utils::HSLShift(
282 SkUnPreMultiply::PMColorToColor(in[x]), hsl_shift));
283 }
284}
285
286// Line processor: no-op (i.e., copy).
[email protected]80b3f7c2011-06-22 02:29:21287void LineProcCopy(const color_utils::HSL& hsl_shift,
288 const SkPMColor* in,
289 SkPMColor* out,
290 int width) {
[email protected]6e25ce82010-04-26 17:12:40291 DCHECK(hsl_shift.h < 0);
292 DCHECK(hsl_shift.s < 0 || fabs(hsl_shift.s - 0.5) < HSLShift::epsilon);
293 DCHECK(hsl_shift.l < 0 || fabs(hsl_shift.l - 0.5) < HSLShift::epsilon);
294 memcpy(out, in, static_cast<size_t>(width) * sizeof(out[0]));
295}
296
297// Line processor: H no-op, S no-op, L decrease.
[email protected]80b3f7c2011-06-22 02:29:21298void LineProcHnopSnopLdec(const color_utils::HSL& hsl_shift,
299 const SkPMColor* in,
300 SkPMColor* out,
301 int width) {
[email protected]6e25ce82010-04-26 17:12:40302 const uint32_t den = 65536;
303
304 DCHECK(hsl_shift.h < 0);
305 DCHECK(hsl_shift.s < 0 || fabs(hsl_shift.s - 0.5) < HSLShift::epsilon);
306 DCHECK(hsl_shift.l <= 0.5 - HSLShift::epsilon && hsl_shift.l >= 0);
307
308 uint32_t ldec_num = static_cast<uint32_t>(hsl_shift.l * 2 * den);
309 for (int x = 0; x < width; x++) {
310 uint32_t a = SkGetPackedA32(in[x]);
311 uint32_t r = SkGetPackedR32(in[x]);
312 uint32_t g = SkGetPackedG32(in[x]);
313 uint32_t b = SkGetPackedB32(in[x]);
314 r = r * ldec_num / den;
315 g = g * ldec_num / den;
316 b = b * ldec_num / den;
317 out[x] = SkPackARGB32(a, r, g, b);
318 }
319}
320
321// Line processor: H no-op, S no-op, L increase.
[email protected]80b3f7c2011-06-22 02:29:21322void LineProcHnopSnopLinc(const color_utils::HSL& hsl_shift,
323 const SkPMColor* in,
324 SkPMColor* out,
325 int width) {
[email protected]6e25ce82010-04-26 17:12:40326 const uint32_t den = 65536;
327
328 DCHECK(hsl_shift.h < 0);
329 DCHECK(hsl_shift.s < 0 || fabs(hsl_shift.s - 0.5) < HSLShift::epsilon);
330 DCHECK(hsl_shift.l >= 0.5 + HSLShift::epsilon && hsl_shift.l <= 1);
331
332 uint32_t linc_num = static_cast<uint32_t>((hsl_shift.l - 0.5) * 2 * den);
333 for (int x = 0; x < width; x++) {
334 uint32_t a = SkGetPackedA32(in[x]);
335 uint32_t r = SkGetPackedR32(in[x]);
336 uint32_t g = SkGetPackedG32(in[x]);
337 uint32_t b = SkGetPackedB32(in[x]);
338 r += (a - r) * linc_num / den;
339 g += (a - g) * linc_num / den;
340 b += (a - b) * linc_num / den;
341 out[x] = SkPackARGB32(a, r, g, b);
342 }
343}
344
345// Saturation changes modifications in RGB
346//
347// (Note that as a further complication, the values we deal in are
348// premultiplied, so R/G/B values must be in the range 0..A. For mathematical
349// purposes, one may as well use r=R/A, g=G/A, b=B/A. Without loss of
350// generality, assume that R/G/B values are in the range 0..1.)
351//
352// Let Max = max(R,G,B), Min = min(R,G,B), and Med be the median value. Then L =
353// (Max+Min)/2. If L is to remain constant, Max+Min must also remain constant.
354//
355// For H to remain constant, first, the (numerical) order of R/G/B (from
356// smallest to largest) must remain the same. Second, all the ratios
357// (R-G)/(Max-Min), (R-B)/(Max-Min), (G-B)/(Max-Min) must remain constant (of
358// course, if Max = Min, then S = 0 and no saturation change is well-defined,
359// since H is not well-defined).
360//
361// Let C_max be a colour with value Max, C_min be one with value Min, and C_med
362// the remaining colour. Increasing saturation (to the maximum) is accomplished
363// by increasing the value of C_max while simultaneously decreasing C_min and
364// changing C_med so that the ratios are maintained; for the latter, it suffices
365// to keep (C_med-C_min)/(C_max-C_min) constant (and equal to
366// (Med-Min)/(Max-Min)).
367
368// Line processor: H no-op, S decrease, L no-op.
[email protected]80b3f7c2011-06-22 02:29:21369void LineProcHnopSdecLnop(const color_utils::HSL& hsl_shift,
370 const SkPMColor* in,
371 SkPMColor* out,
372 int width) {
[email protected]6e25ce82010-04-26 17:12:40373 DCHECK(hsl_shift.h < 0);
374 DCHECK(hsl_shift.s >= 0 && hsl_shift.s <= 0.5 - HSLShift::epsilon);
375 DCHECK(hsl_shift.l < 0 || fabs(hsl_shift.l - 0.5) < HSLShift::epsilon);
376
377 const int32_t denom = 65536;
378 int32_t s_numer = static_cast<int32_t>(hsl_shift.s * 2 * denom);
379 for (int x = 0; x < width; x++) {
380 int32_t a = static_cast<int32_t>(SkGetPackedA32(in[x]));
381 int32_t r = static_cast<int32_t>(SkGetPackedR32(in[x]));
382 int32_t g = static_cast<int32_t>(SkGetPackedG32(in[x]));
383 int32_t b = static_cast<int32_t>(SkGetPackedB32(in[x]));
384
385 int32_t vmax, vmin;
386 if (r > g) { // This uses 3 compares rather than 4.
387 vmax = std::max(r, b);
388 vmin = std::min(g, b);
389 } else {
390 vmax = std::max(g, b);
391 vmin = std::min(r, b);
392 }
393
394 // Use denom * L to avoid rounding.
395 int32_t denom_l = (vmax + vmin) * (denom / 2);
396 int32_t s_numer_l = (vmax + vmin) * s_numer / 2;
397
398 r = (denom_l + r * s_numer - s_numer_l) / denom;
399 g = (denom_l + g * s_numer - s_numer_l) / denom;
400 b = (denom_l + b * s_numer - s_numer_l) / denom;
401 out[x] = SkPackARGB32(a, r, g, b);
402 }
403}
404
405// Line processor: H no-op, S decrease, L decrease.
[email protected]80b3f7c2011-06-22 02:29:21406void LineProcHnopSdecLdec(const color_utils::HSL& hsl_shift,
407 const SkPMColor* in,
408 SkPMColor* out,
409 int width) {
[email protected]6e25ce82010-04-26 17:12:40410 DCHECK(hsl_shift.h < 0);
411 DCHECK(hsl_shift.s >= 0 && hsl_shift.s <= 0.5 - HSLShift::epsilon);
412 DCHECK(hsl_shift.l >= 0 && hsl_shift.l <= 0.5 - HSLShift::epsilon);
413
414 // Can't be too big since we need room for denom*denom and a bit for sign.
415 const int32_t denom = 1024;
416 int32_t l_numer = static_cast<int32_t>(hsl_shift.l * 2 * denom);
417 int32_t s_numer = static_cast<int32_t>(hsl_shift.s * 2 * denom);
418 for (int x = 0; x < width; x++) {
419 int32_t a = static_cast<int32_t>(SkGetPackedA32(in[x]));
420 int32_t r = static_cast<int32_t>(SkGetPackedR32(in[x]));
421 int32_t g = static_cast<int32_t>(SkGetPackedG32(in[x]));
422 int32_t b = static_cast<int32_t>(SkGetPackedB32(in[x]));
423
424 int32_t vmax, vmin;
425 if (r > g) { // This uses 3 compares rather than 4.
426 vmax = std::max(r, b);
427 vmin = std::min(g, b);
428 } else {
429 vmax = std::max(g, b);
430 vmin = std::min(r, b);
431 }
432
433 // Use denom * L to avoid rounding.
434 int32_t denom_l = (vmax + vmin) * (denom / 2);
435 int32_t s_numer_l = (vmax + vmin) * s_numer / 2;
436
437 r = (denom_l + r * s_numer - s_numer_l) * l_numer / (denom * denom);
438 g = (denom_l + g * s_numer - s_numer_l) * l_numer / (denom * denom);
439 b = (denom_l + b * s_numer - s_numer_l) * l_numer / (denom * denom);
440 out[x] = SkPackARGB32(a, r, g, b);
441 }
442}
443
444// Line processor: H no-op, S decrease, L increase.
[email protected]80b3f7c2011-06-22 02:29:21445void LineProcHnopSdecLinc(const color_utils::HSL& hsl_shift,
446 const SkPMColor* in,
447 SkPMColor* out,
448 int width) {
[email protected]6e25ce82010-04-26 17:12:40449 DCHECK(hsl_shift.h < 0);
450 DCHECK(hsl_shift.s >= 0 && hsl_shift.s <= 0.5 - HSLShift::epsilon);
451 DCHECK(hsl_shift.l >= 0.5 + HSLShift::epsilon && hsl_shift.l <= 1);
452
453 // Can't be too big since we need room for denom*denom and a bit for sign.
454 const int32_t denom = 1024;
455 int32_t l_numer = static_cast<int32_t>((hsl_shift.l - 0.5) * 2 * denom);
456 int32_t s_numer = static_cast<int32_t>(hsl_shift.s * 2 * denom);
457 for (int x = 0; x < width; x++) {
458 int32_t a = static_cast<int32_t>(SkGetPackedA32(in[x]));
459 int32_t r = static_cast<int32_t>(SkGetPackedR32(in[x]));
460 int32_t g = static_cast<int32_t>(SkGetPackedG32(in[x]));
461 int32_t b = static_cast<int32_t>(SkGetPackedB32(in[x]));
462
463 int32_t vmax, vmin;
464 if (r > g) { // This uses 3 compares rather than 4.
465 vmax = std::max(r, b);
466 vmin = std::min(g, b);
467 } else {
468 vmax = std::max(g, b);
469 vmin = std::min(r, b);
470 }
471
472 // Use denom * L to avoid rounding.
473 int32_t denom_l = (vmax + vmin) * (denom / 2);
474 int32_t s_numer_l = (vmax + vmin) * s_numer / 2;
475
476 r = denom_l + r * s_numer - s_numer_l;
477 g = denom_l + g * s_numer - s_numer_l;
478 b = denom_l + b * s_numer - s_numer_l;
479
480 r = (r * denom + (a * denom - r) * l_numer) / (denom * denom);
481 g = (g * denom + (a * denom - g) * l_numer) / (denom * denom);
482 b = (b * denom + (a * denom - b) * l_numer) / (denom * denom);
483 out[x] = SkPackARGB32(a, r, g, b);
484 }
485}
486
487const LineProcessor kLineProcessors[kNumHOps][kNumSOps][kNumLOps] = {
488 { // H: kOpHNone
489 { // S: kOpSNone
490 LineProcCopy, // L: kOpLNone
491 LineProcHnopSnopLdec, // L: kOpLDec
492 LineProcHnopSnopLinc // L: kOpLInc
493 },
494 { // S: kOpSDec
495 LineProcHnopSdecLnop, // L: kOpLNone
496 LineProcHnopSdecLdec, // L: kOpLDec
497 LineProcHnopSdecLinc // L: kOpLInc
498 },
499 { // S: kOpSInc
500 LineProcDefault, // L: kOpLNone
501 LineProcDefault, // L: kOpLDec
502 LineProcDefault // L: kOpLInc
503 }
504 },
505 { // H: kOpHShift
506 { // S: kOpSNone
507 LineProcDefault, // L: kOpLNone
508 LineProcDefault, // L: kOpLDec
509 LineProcDefault // L: kOpLInc
510 },
511 { // S: kOpSDec
512 LineProcDefault, // L: kOpLNone
513 LineProcDefault, // L: kOpLDec
514 LineProcDefault // L: kOpLInc
515 },
516 { // S: kOpSInc
517 LineProcDefault, // L: kOpLNone
518 LineProcDefault, // L: kOpLDec
519 LineProcDefault // L: kOpLInc
520 }
521 }
522};
523
524} // namespace HSLShift
525} // namespace
[email protected]cab34d6a2009-09-24 01:14:52526
527// static
528SkBitmap SkBitmapOperations::CreateHSLShiftedBitmap(
529 const SkBitmap& bitmap,
[email protected]37cbcfe2011-03-11 20:47:22530 const color_utils::HSL& hsl_shift) {
[email protected]6e25ce82010-04-26 17:12:40531 // Default to NOPs.
532 HSLShift::OperationOnH H_op = HSLShift::kOpHNone;
533 HSLShift::OperationOnS S_op = HSLShift::kOpSNone;
534 HSLShift::OperationOnL L_op = HSLShift::kOpLNone;
535
536 if (hsl_shift.h >= 0 && hsl_shift.h <= 1)
537 H_op = HSLShift::kOpHShift;
538
539 // Saturation shift: 0 -> fully desaturate, 0.5 -> NOP, 1 -> fully saturate.
540 if (hsl_shift.s >= 0 && hsl_shift.s <= (0.5 - HSLShift::epsilon))
541 S_op = HSLShift::kOpSDec;
542 else if (hsl_shift.s >= (0.5 + HSLShift::epsilon))
543 S_op = HSLShift::kOpSInc;
544
545 // Lightness shift: 0 -> black, 0.5 -> NOP, 1 -> white.
546 if (hsl_shift.l >= 0 && hsl_shift.l <= (0.5 - HSLShift::epsilon))
547 L_op = HSLShift::kOpLDec;
548 else if (hsl_shift.l >= (0.5 + HSLShift::epsilon))
549 L_op = HSLShift::kOpLInc;
550
551 HSLShift::LineProcessor line_proc =
552 HSLShift::kLineProcessors[H_op][S_op][L_op];
553
[email protected]cab34d6a2009-09-24 01:14:52554 DCHECK(bitmap.empty() == false);
555 DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config);
556
557 SkBitmap shifted;
558 shifted.setConfig(SkBitmap::kARGB_8888_Config, bitmap.width(),
559 bitmap.height(), 0);
560 shifted.allocPixels();
561 shifted.eraseARGB(0, 0, 0, 0);
562 shifted.setIsOpaque(false);
563
564 SkAutoLockPixels lock_bitmap(bitmap);
565 SkAutoLockPixels lock_shifted(shifted);
566
567 // Loop through the pixels of the original bitmap.
568 for (int y = 0; y < bitmap.height(); ++y) {
569 SkPMColor* pixels = bitmap.getAddr32(0, y);
570 SkPMColor* tinted_pixels = shifted.getAddr32(0, y);
571
[email protected]6e25ce82010-04-26 17:12:40572 (*line_proc)(hsl_shift, pixels, tinted_pixels, bitmap.width());
[email protected]cab34d6a2009-09-24 01:14:52573 }
574
575 return shifted;
576}
577
578// static
579SkBitmap SkBitmapOperations::CreateTiledBitmap(const SkBitmap& source,
580 int src_x, int src_y,
581 int dst_w, int dst_h) {
[email protected]125b62002012-03-19 14:30:40582 DCHECK(source.config() == SkBitmap::kARGB_8888_Config);
[email protected]cab34d6a2009-09-24 01:14:52583
584 SkBitmap cropped;
585 cropped.setConfig(SkBitmap::kARGB_8888_Config, dst_w, dst_h, 0);
586 cropped.allocPixels();
587 cropped.eraseARGB(0, 0, 0, 0);
588
589 SkAutoLockPixels lock_source(source);
590 SkAutoLockPixels lock_cropped(cropped);
591
592 // Loop through the pixels of the original bitmap.
593 for (int y = 0; y < dst_h; ++y) {
594 int y_pix = (src_y + y) % source.height();
595 while (y_pix < 0)
596 y_pix += source.height();
597
598 uint32* source_row = source.getAddr32(0, y_pix);
599 uint32* dst_row = cropped.getAddr32(0, y);
600
601 for (int x = 0; x < dst_w; ++x) {
602 int x_pix = (src_x + x) % source.width();
603 while (x_pix < 0)
604 x_pix += source.width();
605
606 dst_row[x] = source_row[x_pix];
607 }
608 }
609
610 return cropped;
611}
612
613// static
614SkBitmap SkBitmapOperations::DownsampleByTwoUntilSize(const SkBitmap& bitmap,
615 int min_w, int min_h) {
616 if ((bitmap.width() <= min_w) || (bitmap.height() <= min_h) ||
617 (min_w < 0) || (min_h < 0))
618 return bitmap;
619
620 // Since bitmaps are refcounted, this copy will be fast.
621 SkBitmap current = bitmap;
622 while ((current.width() >= min_w * 2) && (current.height() >= min_h * 2) &&
623 (current.width() > 1) && (current.height() > 1))
624 current = DownsampleByTwo(current);
625 return current;
626}
627
628// static
629SkBitmap SkBitmapOperations::DownsampleByTwo(const SkBitmap& bitmap) {
630 // Handle the nop case.
631 if ((bitmap.width() <= 1) || (bitmap.height() <= 1))
632 return bitmap;
633
634 SkBitmap result;
635 result.setConfig(SkBitmap::kARGB_8888_Config,
636 (bitmap.width() + 1) / 2, (bitmap.height() + 1) / 2);
637 result.allocPixels();
638
639 SkAutoLockPixels lock(bitmap);
[email protected]16d1608c2011-09-14 12:16:56640
641 const int resultLastX = result.width() - 1;
642 const int srcLastX = bitmap.width() - 1;
643
[email protected]cab34d6a2009-09-24 01:14:52644 for (int dest_y = 0; dest_y < result.height(); ++dest_y) {
[email protected]16d1608c2011-09-14 12:16:56645 const int src_y = dest_y << 1;
646 const SkPMColor* SK_RESTRICT cur_src0 = bitmap.getAddr32(0, src_y);
647 const SkPMColor* SK_RESTRICT cur_src1 = cur_src0;
648 if (src_y + 1 < bitmap.height())
649 cur_src1 = bitmap.getAddr32(0, src_y + 1);
650
651 SkPMColor* SK_RESTRICT cur_dst = result.getAddr32(0, dest_y);
652
653 for (int dest_x = 0; dest_x <= resultLastX; ++dest_x) {
[email protected]cab34d6a2009-09-24 01:14:52654 // This code is based on downsampleby2_proc32 in SkBitmap.cpp. It is very
655 // clever in that it does two channels at once: alpha and green ("ag")
656 // and red and blue ("rb"). Each channel gets averaged across 4 pixels
657 // to get the result.
[email protected]16d1608c2011-09-14 12:16:56658 int bump_x = (dest_x << 1) < srcLastX;
[email protected]cab34d6a2009-09-24 01:14:52659 SkPMColor tmp, ag, rb;
660
661 // Top left pixel of the 2x2 block.
[email protected]16d1608c2011-09-14 12:16:56662 tmp = cur_src0[0];
[email protected]cab34d6a2009-09-24 01:14:52663 ag = (tmp >> 8) & 0xFF00FF;
664 rb = tmp & 0xFF00FF;
[email protected]cab34d6a2009-09-24 01:14:52665
666 // Top right pixel of the 2x2 block.
[email protected]16d1608c2011-09-14 12:16:56667 tmp = cur_src0[bump_x];
[email protected]cab34d6a2009-09-24 01:14:52668 ag += (tmp >> 8) & 0xFF00FF;
669 rb += tmp & 0xFF00FF;
[email protected]cab34d6a2009-09-24 01:14:52670
671 // Bottom left pixel of the 2x2 block.
[email protected]16d1608c2011-09-14 12:16:56672 tmp = cur_src1[0];
[email protected]cab34d6a2009-09-24 01:14:52673 ag += (tmp >> 8) & 0xFF00FF;
674 rb += tmp & 0xFF00FF;
[email protected]cab34d6a2009-09-24 01:14:52675
676 // Bottom right pixel of the 2x2 block.
[email protected]16d1608c2011-09-14 12:16:56677 tmp = cur_src1[bump_x];
[email protected]cab34d6a2009-09-24 01:14:52678 ag += (tmp >> 8) & 0xFF00FF;
679 rb += tmp & 0xFF00FF;
680
681 // Put the channels back together, dividing each by 4 to get the average.
682 // |ag| has the alpha and green channels shifted right by 8 bits from
683 // there they should end up, so shifting left by 6 gives them in the
684 // correct position divided by 4.
[email protected]16d1608c2011-09-14 12:16:56685 *cur_dst++ = ((rb >> 2) & 0xFF00FF) | ((ag << 6) & 0xFF00FF00);
686
687 cur_src0 += 2;
688 cur_src1 += 2;
[email protected]cab34d6a2009-09-24 01:14:52689 }
690 }
691
692 return result;
693}
694
[email protected]3eaf0ecd2010-07-15 15:30:55695// static
696SkBitmap SkBitmapOperations::UnPreMultiply(const SkBitmap& bitmap) {
697 if (bitmap.isNull())
698 return bitmap;
699 if (bitmap.isOpaque())
700 return bitmap;
701
702 SkBitmap opaque_bitmap;
703 opaque_bitmap.setConfig(bitmap.config(), bitmap.width(), bitmap.height());
704 opaque_bitmap.allocPixels();
705
706 {
707 SkAutoLockPixels bitmap_lock(bitmap);
708 SkAutoLockPixels opaque_bitmap_lock(opaque_bitmap);
709 for (int y = 0; y < opaque_bitmap.height(); y++) {
710 for (int x = 0; x < opaque_bitmap.width(); x++) {
711 uint32 src_pixel = *bitmap.getAddr32(x, y);
712 uint32* dst_pixel = opaque_bitmap.getAddr32(x, y);
713 SkColor unmultiplied = SkUnPreMultiply::PMColorToColor(src_pixel);
714 *dst_pixel = unmultiplied;
715 }
716 }
717 }
718
719 opaque_bitmap.setIsOpaque(true);
720 return opaque_bitmap;
721}
[email protected]8d1b864d12010-10-10 00:04:34722
723// static
724SkBitmap SkBitmapOperations::CreateTransposedBtmap(const SkBitmap& image) {
725 DCHECK(image.config() == SkBitmap::kARGB_8888_Config);
726
[email protected]8d1b864d12010-10-10 00:04:34727 SkBitmap transposed;
728 transposed.setConfig(
729 SkBitmap::kARGB_8888_Config, image.height(), image.width(), 0);
730 transposed.allocPixels();
[email protected]579dacb2011-11-08 20:06:36731
732 SkAutoLockPixels lock_image(image);
733 SkAutoLockPixels lock_transposed(transposed);
[email protected]8d1b864d12010-10-10 00:04:34734
735 for (int y = 0; y < image.height(); ++y) {
736 uint32* image_row = image.getAddr32(0, y);
737 for (int x = 0; x < image.width(); ++x) {
738 uint32* dst = transposed.getAddr32(y, x);
739 *dst = image_row[x];
740 }
741 }
742
743 return transposed;
744}
745
[email protected]6cd13aa2012-03-23 02:27:16746// static
[email protected]6cd13aa2012-03-23 02:27:16747SkBitmap SkBitmapOperations::CreateColorMask(const SkBitmap& bitmap,
748 SkColor c) {
749 DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config);
750
751 SkBitmap color_mask;
752 color_mask.setConfig(SkBitmap::kARGB_8888_Config,
753 bitmap.width(), bitmap.height());
754 color_mask.allocPixels();
755 color_mask.eraseARGB(0, 0, 0, 0);
756
757 SkCanvas canvas(color_mask);
758
759 SkColorFilter* color_filter = SkColorFilter::CreateModeFilter(
760 c, SkXfermode::kSrcIn_Mode);
761 SkPaint paint;
762 paint.setColorFilter(color_filter)->unref();
763 canvas.drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &paint);
764 return color_mask;
765}
766
767// static
[email protected]58e068f2012-05-26 00:29:16768SkBitmap SkBitmapOperations::CreateDropShadow(
769 const SkBitmap& bitmap,
770 const gfx::ShadowValues& shadows) {
[email protected]6cd13aa2012-03-23 02:27:16771 DCHECK(bitmap.config() == SkBitmap::kARGB_8888_Config);
772
[email protected]58e068f2012-05-26 00:29:16773 // Shadow margin insets are negative values because they grow outside.
774 // Negate them here as grow direction is not important and only pixel value
775 // is of interest here.
776 gfx::Insets shadow_margin = -gfx::ShadowValue::GetMargin(shadows);
[email protected]6cd13aa2012-03-23 02:27:16777
778 SkBitmap image_with_shadow;
779 image_with_shadow.setConfig(SkBitmap::kARGB_8888_Config,
[email protected]58e068f2012-05-26 00:29:16780 bitmap.width() + shadow_margin.width(),
781 bitmap.height() + shadow_margin.height());
[email protected]6cd13aa2012-03-23 02:27:16782 image_with_shadow.allocPixels();
783 image_with_shadow.eraseARGB(0, 0, 0, 0);
784
785 SkCanvas canvas(image_with_shadow);
[email protected]58e068f2012-05-26 00:29:16786 canvas.translate(SkIntToScalar(shadow_margin.left()),
787 SkIntToScalar(shadow_margin.top()));
[email protected]6cd13aa2012-03-23 02:27:16788
789 SkPaint paint;
[email protected]58e068f2012-05-26 00:29:16790 for (size_t i = 0; i < shadows.size(); ++i) {
791 const gfx::ShadowValue& shadow = shadows[i];
792 SkBitmap shadow_image = SkBitmapOperations::CreateColorMask(bitmap,
793 shadow.color());
[email protected]6cd13aa2012-03-23 02:27:16794
795 paint.setImageFilter(
[email protected]58e068f2012-05-26 00:29:16796 new SkBlurImageFilter(SkDoubleToScalar(shadow.blur()),
797 SkDoubleToScalar(shadow.blur())))->unref();
[email protected]6cd13aa2012-03-23 02:27:16798
799 canvas.saveLayer(0, &paint);
[email protected]58e068f2012-05-26 00:29:16800 canvas.drawBitmap(shadow_image,
801 SkIntToScalar(shadow.x()),
802 SkIntToScalar(shadow.y()));
[email protected]6cd13aa2012-03-23 02:27:16803 canvas.restore();
804 }
805
806 canvas.drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0));
807 return image_with_shadow;
808}
809