// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef UNSAFE_BUFFERS_BUILD // TODO(crbug.com/354829279): Remove this and convert code to safer constructs. #pragma allow_unsafe_buffers #endif #include "ui/gfx/skbitmap_operations.h" #include <stddef.h> #include <stdint.h> #include <string.h> #include <algorithm> #include "base/check_op.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkColorPriv.h" #include "third_party/skia/include/core/SkUnPreMultiply.h" #include "third_party/skia/include/effects/SkImageFilters.h" #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/size.h" static bool IsUninitializedBitmap(const SkBitmap& bitmap) { … } // static SkBitmap SkBitmapOperations::CreateInvertedBitmap(const SkBitmap& image) { … } // static SkBitmap SkBitmapOperations::CreateBlendedBitmap(const SkBitmap& first, const SkBitmap& second, double alpha) { … } // static SkBitmap SkBitmapOperations::CreateMaskedBitmap(const SkBitmap& rgb, const SkBitmap& alpha) { … } // static SkBitmap SkBitmapOperations::CreateButtonBackground(SkColor color, const SkBitmap& image, const SkBitmap& mask) { … } namespace { namespace HSLShift { // TODO(viettrungluu): Some things have yet to be optimized at all. // Notes on and conventions used in the following code // // Conventions: // - R, G, B, A = obvious; as variables: |r|, |g|, |b|, |a| (see also below) // - H, S, L = obvious; as variables: |h|, |s|, |l| (see also below) // - variables derived from S, L shift parameters: |sdec| and |sinc| for S // increase and decrease factors, |ldec| and |linc| for L (see also below) // // To try to optimize HSL shifts, we do several things: // - Avoid unpremultiplying (then processing) then premultiplying. This means // that R, G, B values (and also L, but not H and S) should be treated as // having a range of 0..A (where A is alpha). // - Do things in integer/fixed-point. This avoids costly conversions between // floating-point and integer, though I should study the tradeoff more // carefully (presumably, at some point of processing complexity, converting // and processing using simpler floating-point code will begin to win in // performance). Also to be studied is the speed/type of floating point // conversions; see, e.g., <http://www.stereopsis.com/sree/fpu2006.html>. // // Conventions for fixed-point arithmetic // - Each function has a constant denominator (called |den|, which should be a // power of 2), appropriate for the computations done in that function. // - A value |x| is then typically represented by a numerator, named |x_num|, // so that its actual value is |x_num / den| (casting to floating-point // before division). // - To obtain |x_num| from |x|, simply multiply by |den|, i.e., |x_num = x * // den| (casting appropriately). // - When necessary, a value |x| may also be represented as a numerator over // the denominator squared (set |den2 = den * den|). In such a case, the // corresponding variable is called |x_num2| (so that its actual value is // |x_num^2 / den2|. // - The representation of the product of |x| and |y| is be called |x_y_num| if // |x * y == x_y_num / den|, and |xy_num2| if |x * y == x_y_num2 / den2|. In // the latter case, notice that one can calculate |x_y_num2 = x_num * y_num|. // Routine used to process a line; typically specialized for specific kinds of // HSL shifts (to optimize). LineProcessor; enum OperationOnH { … }; enum OperationOnS { … }; enum OperationOnL { … }; // Epsilon used to judge when shift values are close enough to various critical // values (typically 0.5, which yields a no-op for S and L shifts. 1/256 should // be small enough, but let's play it safe> const double epsilon = …; // Line processor: default/universal (i.e., old-school). void LineProcDefault(const color_utils::HSL& hsl_shift, const SkPMColor* in, SkPMColor* out, int width) { … } // Line processor: no-op (i.e., copy). void LineProcCopy(const color_utils::HSL& hsl_shift, const SkPMColor* in, SkPMColor* out, int width) { … } // Line processor: H no-op, S no-op, L decrease. void LineProcHnopSnopLdec(const color_utils::HSL& hsl_shift, const SkPMColor* in, SkPMColor* out, int width) { … } // Line processor: H no-op, S no-op, L increase. void LineProcHnopSnopLinc(const color_utils::HSL& hsl_shift, const SkPMColor* in, SkPMColor* out, int width) { … } // Saturation changes modifications in RGB // // (Note that as a further complication, the values we deal in are // premultiplied, so R/G/B values must be in the range 0..A. For mathematical // purposes, one may as well use r=R/A, g=G/A, b=B/A. Without loss of // generality, assume that R/G/B values are in the range 0..1.) // // Let Max = max(R,G,B), Min = min(R,G,B), and Med be the median value. Then L = // (Max+Min)/2. If L is to remain constant, Max+Min must also remain constant. // // For H to remain constant, first, the (numerical) order of R/G/B (from // smallest to largest) must remain the same. Second, all the ratios // (R-G)/(Max-Min), (R-B)/(Max-Min), (G-B)/(Max-Min) must remain constant (of // course, if Max = Min, then S = 0 and no saturation change is well-defined, // since H is not well-defined). // // Let C_max be a colour with value Max, C_min be one with value Min, and C_med // the remaining colour. Increasing saturation (to the maximum) is accomplished // by increasing the value of C_max while simultaneously decreasing C_min and // changing C_med so that the ratios are maintained; for the latter, it suffices // to keep (C_med-C_min)/(C_max-C_min) constant (and equal to // (Med-Min)/(Max-Min)). // Line processor: H no-op, S decrease, L no-op. void LineProcHnopSdecLnop(const color_utils::HSL& hsl_shift, const SkPMColor* in, SkPMColor* out, int width) { … } // Line processor: H no-op, S decrease, L decrease. void LineProcHnopSdecLdec(const color_utils::HSL& hsl_shift, const SkPMColor* in, SkPMColor* out, int width) { … } // Line processor: H no-op, S decrease, L increase. void LineProcHnopSdecLinc(const color_utils::HSL& hsl_shift, const SkPMColor* in, SkPMColor* out, int width) { … } const LineProcessor kLineProcessors[kNumHOps][kNumSOps][kNumLOps] = …; } // namespace HSLShift } // namespace // static SkBitmap SkBitmapOperations::CreateHSLShiftedBitmap( const SkBitmap& bitmap, const color_utils::HSL& hsl_shift) { … } // static SkBitmap SkBitmapOperations::CreateTiledBitmap(const SkBitmap& source, int src_x, int src_y, int dst_w, int dst_h) { … } // static SkBitmap SkBitmapOperations::DownsampleByTwoUntilSize(const SkBitmap& bitmap, int min_w, int min_h) { … } // static SkBitmap SkBitmapOperations::DownsampleByTwo(const SkBitmap& bitmap) { … } // static SkBitmap SkBitmapOperations::UnPreMultiply(const SkBitmap& bitmap) { … } // static SkBitmap SkBitmapOperations::CreateTransposedBitmap(const SkBitmap& image) { … } // static SkBitmap SkBitmapOperations::CreateColorMask(const SkBitmap& bitmap, SkColor c) { … } // static SkBitmap SkBitmapOperations::CreateDropShadow( const SkBitmap& bitmap, const gfx::ShadowValues& shadows) { … } // static SkBitmap SkBitmapOperations::Rotate(const SkBitmap& source, RotationAmount rotation) { … }