/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/core/SkFont.h"
#include "include/core/SkFontTypes.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/private/base/SkAssert.h"
#include "include/private/base/SkTo.h"
#include "src/core/SkFontPriv.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"

#include <cstdint>

// packed int at the beginning of the serialized font:
//
//  control_bits:8 size_as_byte:8 flags:12 edging:2 hinting:2

enum {
    kSize_Is_Byte_Bit   = 1 << 31,
    kHas_ScaleX_Bit     = 1 << 30,
    kHas_SkewX_Bit      = 1 << 29,
    kHas_Typeface_Bit   = 1 << 28,

    kShift_for_Size     = 16,
    kMask_For_Size      = 0xFF,

    kShift_For_Flags    = 4,
    kMask_For_Flags     = 0xFFF,

    kShift_For_Edging   = 2,
    kMask_For_Edging    = 0x3,

    kShift_For_Hinting  = 0,
    kMask_For_Hinting   = 0x3
};

static bool scalar_is_byte(SkScalar x) {
    int ix = (int)x;
    return ix == x && ix >= 0 && ix <= kMask_For_Size;
}

void SkFontPriv::Flatten(const SkFont& font, SkWriteBuffer& buffer) {
    SkASSERT(font.fFlags <= SkFont::kAllFlags);
    SkASSERT((font.fFlags & ~kMask_For_Flags) == 0);
    SkASSERT((font.fEdging & ~kMask_For_Edging) == 0);
    SkASSERT((font.fHinting & ~kMask_For_Hinting) == 0);

    uint32_t packed = 0;
    packed |= font.fFlags << kShift_For_Flags;
    packed |= font.fEdging << kShift_For_Edging;
    packed |= font.fHinting << kShift_For_Hinting;

    if (scalar_is_byte(font.fSize)) {
        packed |= kSize_Is_Byte_Bit;
        packed |= (int)font.fSize << kShift_for_Size;
    }
    if (font.fScaleX != 1) {
        packed |= kHas_ScaleX_Bit;
    }
    if (font.fSkewX != 0) {
        packed |= kHas_SkewX_Bit;
    }
    if (font.fTypeface) {
        packed |= kHas_Typeface_Bit;
    }

    buffer.write32(packed);
    if (!(packed & kSize_Is_Byte_Bit)) {
        buffer.writeScalar(font.fSize);
    }
    if (packed & kHas_ScaleX_Bit) {
        buffer.writeScalar(font.fScaleX);
    }
    if (packed & kHas_SkewX_Bit) {
        buffer.writeScalar(font.fSkewX);
    }
    if (packed & kHas_Typeface_Bit) {
        buffer.writeTypeface(font.fTypeface.get());
    }
}

bool SkFontPriv::Unflatten(SkFont* font, SkReadBuffer& buffer) {
    const uint32_t packed = buffer.read32();

    if (packed & kSize_Is_Byte_Bit) {
        font->fSize = (packed >> kShift_for_Size) & kMask_For_Size;
    } else {
        font->fSize = buffer.readScalar();
    }
    if (packed & kHas_ScaleX_Bit) {
        font->fScaleX = buffer.readScalar();
    }
    if (packed & kHas_SkewX_Bit) {
        font->fSkewX = buffer.readScalar();
    }
    if (packed & kHas_Typeface_Bit) {
        font->setTypeface(buffer.readTypeface());
    }

    SkASSERT(SkFont::kAllFlags <= kMask_For_Flags);
    // we & with kAllFlags, to clear out any unknown flag bits
    font->fFlags = SkToU8((packed >> kShift_For_Flags) & SkFont::kAllFlags);

    unsigned edging = (packed >> kShift_For_Edging) & kMask_For_Edging;
    if (edging > (unsigned)SkFont::Edging::kSubpixelAntiAlias) {
        edging = 0;
    }
    font->fEdging = SkToU8(edging);

    unsigned hinting = (packed >> kShift_For_Hinting) & kMask_For_Hinting;
    if (hinting > (unsigned)SkFontHinting::kFull) {
        hinting = 0;
    }
    font->fHinting = SkToU8(hinting);

    return buffer.isValid();
}
