aboutsummaryrefslogtreecommitdiffstats
path: root/src/core/SkBlitter_ARGB32_Subpixel.cpp
blob: 5d334ae8a5673ba78417c30cc6f41d34a70a87a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* libs/graphics/sgl/SkBlitter_ARGB32_Subpixel.cpp
**
** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

/* LCD blend functions:

   These functions take an alpha pixel of the following form:
      red, green, blue -> an alpha value for the given colour component.
      alpha -> the max of the red, green and blue alpha values.

   These alpha pixels result from subpixel renderering. The R/G/B values have
   already been corrected for RGB/BGR element ordering.

   The alpha pixel is blended with an original pixel and a source colour,
   resulting in a new pixel value.
*/

#include "SkBitmap.h"
#include "SkColorPriv.h"
#include "SkMask.h"
#include "SkRect.h"

namespace skia_blitter_support {

/** Given a clip region which describes the desired location of a glyph and a
    bitmap to which an LCD glyph is to be blitted, return a pointer to the
    SkBitmap's pixels and output width and height adjusts for the glyph as well
    as a pointer into the glyph.

    Recall that LCD glyphs have extra rows (vertical mode) or columns
    (horizontal mode) at the edges as a result of low-pass filtering. If we
    wanted to put a glyph on the hard-left edge of bitmap, we would have to know
    to start one pixel into the glyph, as well as to only add 1 to the recorded
    glyph width etc. This function encapsulates that behaviour.

    @param mask    The glyph to be blitted.
    @param clip    The clip region describing the desired location of the glyph.
    @param device  The SkBitmap target for the blit.
    @param widthAdjustment  (output) a number to add to the glyph's nominal width.
    @param heightAdjustment (output) a number to add to the glyph's nominal width.
    @param alpha32 (output) a pointer into the 32-bit subpixel alpha data for the glyph
*/
uint32_t* adjustForSubpixelClip(const SkMask& mask,
                                const SkIRect& clip, const SkBitmap& device,
                                int* widthAdjustment, int* heightAdjustment,
                                const uint32_t** alpha32) {
    const bool lcdMode = mask.fFormat == SkMask::kHorizontalLCD_Format;
    const bool verticalLCDMode = mask.fFormat == SkMask::kVerticalLCD_Format;
    const int  leftOffset = clip.fLeft > 0 ? lcdMode : 0;
    const int  topOffset = clip.fTop > 0 ? verticalLCDMode : 0;
    const int  rightOffset = lcdMode && clip.fRight < device.width();
    const int  bottomOffset = verticalLCDMode && clip.fBottom < device.height();

    uint32_t* device32 = device.getAddr32(clip.fLeft - leftOffset, clip.fTop - topOffset);
    *alpha32 = mask.getAddrLCD(clip.fLeft + (lcdMode && !leftOffset),
                               clip.fTop + (verticalLCDMode && !topOffset));

    *widthAdjustment = leftOffset + rightOffset;
    *heightAdjustment = topOffset + bottomOffset;

    return device32;
}

uint32_t BlendLCDPixelWithColor(const uint32_t alphaPixel, const uint32_t originalPixel,
                                const uint32_t sourcePixel) {
    unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
    unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
    unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));

    unsigned sourceRed = SkGetPackedR32(sourcePixel);
    unsigned sourceGreen = SkGetPackedG32(sourcePixel);
    unsigned sourceBlue = SkGetPackedB32(sourcePixel);
    unsigned sourceAlpha = SkAlpha255To256(SkGetPackedA32(sourcePixel));

    alphaRed = (alphaRed * sourceAlpha) >> 8;
    alphaGreen = (alphaGreen * sourceAlpha) >> 8;
    alphaBlue = (alphaBlue * sourceAlpha) >> 8;
    unsigned alphaAlpha = SkMax32(SkMax32(alphaRed, alphaBlue), alphaGreen);

    unsigned originalRed = SkGetPackedR32(originalPixel);
    unsigned originalGreen = SkGetPackedG32(originalPixel);
    unsigned originalBlue = SkGetPackedB32(originalPixel);
    unsigned originalAlpha = SkGetPackedA32(originalPixel);

    return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
                        ((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8),
                        ((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8),
                        ((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8));

}

uint32_t BlendLCDPixelWithOpaqueColor(const uint32_t alphaPixel, const uint32_t originalPixel,
                                      const uint32_t sourcePixel) {
    unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
    unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
    unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
    unsigned alphaAlpha = SkGetPackedA32(alphaPixel);

    unsigned sourceRed = SkGetPackedR32(sourcePixel);
    unsigned sourceGreen = SkGetPackedG32(sourcePixel);
    unsigned sourceBlue = SkGetPackedB32(sourcePixel);

    unsigned originalRed = SkGetPackedR32(originalPixel);
    unsigned originalGreen = SkGetPackedG32(originalPixel);
    unsigned originalBlue = SkGetPackedB32(originalPixel);
    unsigned originalAlpha = SkGetPackedA32(originalPixel);

    return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
                        ((sourceRed * alphaRed) >> 8) + ((originalRed * (256 - alphaRed)) >> 8),
                        ((sourceGreen * alphaGreen) >> 8) + ((originalGreen * (256 - alphaGreen)) >> 8),
                        ((sourceBlue * alphaBlue) >> 8) + ((originalBlue * (256 - alphaBlue)) >> 8));
}

uint32_t BlendLCDPixelWithBlack(const uint32_t alphaPixel, const uint32_t originalPixel) {
    unsigned alphaRed = SkAlpha255To256(SkGetPackedR32(alphaPixel));
    unsigned alphaGreen = SkAlpha255To256(SkGetPackedG32(alphaPixel));
    unsigned alphaBlue = SkAlpha255To256(SkGetPackedB32(alphaPixel));
    unsigned alphaAlpha = SkGetPackedA32(alphaPixel);

    unsigned originalRed = SkGetPackedR32(originalPixel);
    unsigned originalGreen = SkGetPackedG32(originalPixel);
    unsigned originalBlue = SkGetPackedB32(originalPixel);
    unsigned originalAlpha = SkGetPackedA32(originalPixel);

    return SkPackARGB32(SkMin32(255u, alphaAlpha + originalAlpha),
                        (originalRed * (256 - alphaRed)) >> 8,
                        (originalGreen * (256 - alphaGreen)) >> 8,
                        (originalBlue * (256 - alphaBlue)) >> 8);
}

}  // namespace skia_blitter_support