/* 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