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

#include "SkFontConfigInterface.h"
#include "SkFontDescriptor.h"
#include "SkFontHost.h"
#include "SkFontHost_FreeType_common.h"
#include "SkFontStream.h"
#include "SkStream.h"
#include "SkTypeface.h"
#include "SkTypefaceCache.h"

SK_DECLARE_STATIC_MUTEX(gFontConfigInterfaceMutex);
static SkFontConfigInterface* gFontConfigInterface;

SkFontConfigInterface* SkFontConfigInterface::RefGlobal() {
    SkAutoMutexAcquire ac(gFontConfigInterfaceMutex);

    return SkSafeRef(gFontConfigInterface);
}

SkFontConfigInterface* SkFontConfigInterface::SetGlobal(SkFontConfigInterface* fc) {
    SkAutoMutexAcquire ac(gFontConfigInterfaceMutex);

    SkRefCnt_SafeAssign(gFontConfigInterface, fc);
    return fc;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// convenience function to create the direct interface if none is installed.
extern SkFontConfigInterface* SkCreateDirectFontConfigInterface();

static SkFontConfigInterface* RefFCI() {
    for (;;) {
        SkFontConfigInterface* fci = SkFontConfigInterface::RefGlobal();
        if (fci) {
            return fci;
        }
        fci = SkFontConfigInterface::GetSingletonDirectInterface();
        SkFontConfigInterface::SetGlobal(fci)->unref();
    }
}

class FontConfigTypeface : public SkTypeface_FreeType {
    SkFontConfigInterface::FontIdentity fIdentity;
    SkString fFamilyName;
    SkStream* fLocalStream;

public:
    FontConfigTypeface(Style style,
                       const SkFontConfigInterface::FontIdentity& fi,
                       const SkString& familyName)
            : INHERITED(style, SkTypefaceCache::NewFontID(), false)
            , fIdentity(fi)
            , fFamilyName(familyName)
            , fLocalStream(NULL) {}

    FontConfigTypeface(Style style, SkStream* localStream)
            : INHERITED(style, SkTypefaceCache::NewFontID(), false) {
        // we default to empty fFamilyName and fIdentity
        fLocalStream = localStream;
        SkSafeRef(localStream);
    }

    virtual ~FontConfigTypeface() {
        SkSafeUnref(fLocalStream);
    }

    const SkFontConfigInterface::FontIdentity& getIdentity() const {
        return fIdentity;
    }

    const char* getFamilyName() const { return fFamilyName.c_str(); }
    SkStream*   getLocalStream() const { return fLocalStream; }

    bool isFamilyName(const char* name) const {
        return fFamilyName.equals(name);
    }

protected:
    friend class SkFontHost;    // hack until we can make public versions

    virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
    virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                  size_t length, void* data) const SK_OVERRIDE;
    virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
    virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;

private:
    typedef SkTypeface_FreeType INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

struct FindRec {
    FindRec(const char* name, SkTypeface::Style style)
        : fFamilyName(name)  // don't need to make a deep copy
        , fStyle(style) {}

    const char* fFamilyName;
    SkTypeface::Style fStyle;
};

static bool find_proc(SkTypeface* face, SkTypeface::Style style, void* ctx) {
    FontConfigTypeface* fci = (FontConfigTypeface*)face;
    const FindRec* rec = (const FindRec*)ctx;

    return rec->fStyle == style && fci->isFamilyName(rec->fFamilyName);
}

SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                       const char familyName[],
                                       SkTypeface::Style style) {
    SkAutoTUnref<SkFontConfigInterface> fci(RefFCI());
    if (NULL == fci.get()) {
        return NULL;
    }

    if (familyFace) {
        FontConfigTypeface* fct = (FontConfigTypeface*)familyFace;
        familyName = fct->getFamilyName();
    }

    FindRec rec(familyName, style);
    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_proc, &rec);
    if (face) {
//        SkDebugf("found cached face <%s> <%s> %p [%d]\n", familyName, ((FontConfigTypeface*)face)->getFamilyName(), face, face->getRefCnt());
        return face;
    }

    SkFontConfigInterface::FontIdentity indentity;
    SkString                            outFamilyName;
    SkTypeface::Style                   outStyle;

    if (!fci->matchFamilyName(familyName, style,
                              &indentity, &outFamilyName, &outStyle)) {
        return NULL;
    }

    face = SkNEW_ARGS(FontConfigTypeface, (outStyle, indentity, outFamilyName));
    SkTypefaceCache::Add(face, style);
//    SkDebugf("add face <%s> <%s> %p [%d]\n", familyName, outFamilyName.c_str(), face, face->getRefCnt());
    return face;
}

SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
    if (!stream) {
        return NULL;
    }
    const size_t length = stream->getLength();
    if (!length) {
        return NULL;
    }
    if (length >= 1024 * 1024 * 1024) {
        return NULL;  // don't accept too large fonts (>= 1GB) for safety.
    }

    // TODO should the caller give us the style?
    SkTypeface::Style style = SkTypeface::kNormal;
    SkTypeface* face = SkNEW_ARGS(FontConfigTypeface, (style, stream));
    SkTypefaceCache::Add(face, style);
    return face;
}

SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
    SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
    return stream.get() ? CreateTypefaceFromStream(stream) : NULL;
}

///////////////////////////////////////////////////////////////////////////////

SkStream* FontConfigTypeface::onOpenStream(int* ttcIndex) const {
    SkStream* stream = this->getLocalStream();
    if (stream) {
        // TODO: fix issue 1176.
        // As of now open_stream will return a stream and unwind it, but the
        // SkStream is not thread safe, and if two threads use the stream they
        // may collide and print preview for example could still fail,
        // or there could be some failures in rendering if this stream is used
        // there.
        stream->rewind();
        stream->ref();
        // should have been provided by CreateFromStream()
        *ttcIndex = 0;
    } else {
        SkAutoTUnref<SkFontConfigInterface> fci(RefFCI());
        if (NULL == fci.get()) {
            return NULL;
        }
        stream = fci->openStream(this->getIdentity());
        *ttcIndex = this->getIdentity().fTTCIndex;
    }
    return stream;
}

int FontConfigTypeface::onGetTableTags(SkFontTableTag tags[]) const {
    int ttcIndex;
    SkAutoTUnref<SkStream> stream(this->openStream(&ttcIndex));
    return stream.get()
                ? SkFontStream::GetTableTags(stream, ttcIndex, tags)
                : 0;
}

size_t FontConfigTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
                                  size_t length, void* data) const {
    int ttcIndex;
    SkAutoTUnref<SkStream> stream(this->openStream(&ttcIndex));
    return stream.get()
                ? SkFontStream::GetTableData(stream, ttcIndex,
                                             tag, offset, length, data)
                : 0;
}

void FontConfigTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
                                             bool* isLocalStream) const {
    desc->setFamilyName(this->getFamilyName());
    *isLocalStream = SkToBool(this->getLocalStream());
}

///////////////////////////////////////////////////////////////////////////////

#include "SkFontMgr.h"

SkFontMgr* SkFontMgr::Factory() {
    // todo
    return NULL;
}
<commit_msg>check-point for new fontmgr on linux<commit_after>/*
 * Copyright 2008 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkFontConfigInterface.h"
#include "SkFontDescriptor.h"
#include "SkFontHost.h"
#include "SkFontHost_FreeType_common.h"
#include "SkFontStream.h"
#include "SkStream.h"
#include "SkTypeface.h"
#include "SkTypefaceCache.h"

SK_DECLARE_STATIC_MUTEX(gFontConfigInterfaceMutex);
static SkFontConfigInterface* gFontConfigInterface;

SkFontConfigInterface* SkFontConfigInterface::RefGlobal() {
    SkAutoMutexAcquire ac(gFontConfigInterfaceMutex);

    return SkSafeRef(gFontConfigInterface);
}

SkFontConfigInterface* SkFontConfigInterface::SetGlobal(SkFontConfigInterface* fc) {
    SkAutoMutexAcquire ac(gFontConfigInterfaceMutex);

    SkRefCnt_SafeAssign(gFontConfigInterface, fc);
    return fc;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// convenience function to create the direct interface if none is installed.
extern SkFontConfigInterface* SkCreateDirectFontConfigInterface();

static SkFontConfigInterface* RefFCI() {
    for (;;) {
        SkFontConfigInterface* fci = SkFontConfigInterface::RefGlobal();
        if (fci) {
            return fci;
        }
        fci = SkFontConfigInterface::GetSingletonDirectInterface();
        SkFontConfigInterface::SetGlobal(fci)->unref();
    }
}

class FontConfigTypeface : public SkTypeface_FreeType {
    SkFontConfigInterface::FontIdentity fIdentity;
    SkString fFamilyName;
    SkStream* fLocalStream;

public:
    FontConfigTypeface(Style style,
                       const SkFontConfigInterface::FontIdentity& fi,
                       const SkString& familyName)
            : INHERITED(style, SkTypefaceCache::NewFontID(), false)
            , fIdentity(fi)
            , fFamilyName(familyName)
            , fLocalStream(NULL) {}

    FontConfigTypeface(Style style, SkStream* localStream)
            : INHERITED(style, SkTypefaceCache::NewFontID(), false) {
        // we default to empty fFamilyName and fIdentity
        fLocalStream = localStream;
        SkSafeRef(localStream);
    }

    virtual ~FontConfigTypeface() {
        SkSafeUnref(fLocalStream);
    }

    const SkFontConfigInterface::FontIdentity& getIdentity() const {
        return fIdentity;
    }

    const char* getFamilyName() const { return fFamilyName.c_str(); }
    SkStream*   getLocalStream() const { return fLocalStream; }

    bool isFamilyName(const char* name) const {
        return fFamilyName.equals(name);
    }

protected:
    friend class SkFontHost;    // hack until we can make public versions

    virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE;
    virtual size_t onGetTableData(SkFontTableTag, size_t offset,
                                  size_t length, void* data) const SK_OVERRIDE;
    virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE;
    virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE;

private:
    typedef SkTypeface_FreeType INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

struct FindRec {
    FindRec(const char* name, SkTypeface::Style style)
        : fFamilyName(name)  // don't need to make a deep copy
        , fStyle(style) {}

    const char* fFamilyName;
    SkTypeface::Style fStyle;
};

static bool find_proc(SkTypeface* face, SkTypeface::Style style, void* ctx) {
    FontConfigTypeface* fci = (FontConfigTypeface*)face;
    const FindRec* rec = (const FindRec*)ctx;

    return rec->fStyle == style && fci->isFamilyName(rec->fFamilyName);
}

SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                       const char familyName[],
                                       SkTypeface::Style style) {
    SkAutoTUnref<SkFontConfigInterface> fci(RefFCI());
    if (NULL == fci.get()) {
        return NULL;
    }

    if (familyFace) {
        FontConfigTypeface* fct = (FontConfigTypeface*)familyFace;
        familyName = fct->getFamilyName();
    }

    FindRec rec(familyName, style);
    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(find_proc, &rec);
    if (face) {
//        SkDebugf("found cached face <%s> <%s> %p [%d]\n", familyName, ((FontConfigTypeface*)face)->getFamilyName(), face, face->getRefCnt());
        return face;
    }

    SkFontConfigInterface::FontIdentity indentity;
    SkString                            outFamilyName;
    SkTypeface::Style                   outStyle;

    if (!fci->matchFamilyName(familyName, style,
                              &indentity, &outFamilyName, &outStyle)) {
        return NULL;
    }

    face = SkNEW_ARGS(FontConfigTypeface, (outStyle, indentity, outFamilyName));
    SkTypefaceCache::Add(face, style);
//    SkDebugf("add face <%s> <%s> %p [%d]\n", familyName, outFamilyName.c_str(), face, face->getRefCnt());
    return face;
}

SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
    if (!stream) {
        return NULL;
    }
    const size_t length = stream->getLength();
    if (!length) {
        return NULL;
    }
    if (length >= 1024 * 1024 * 1024) {
        return NULL;  // don't accept too large fonts (>= 1GB) for safety.
    }

    // TODO should the caller give us the style?
    SkTypeface::Style style = SkTypeface::kNormal;
    SkTypeface* face = SkNEW_ARGS(FontConfigTypeface, (style, stream));
    SkTypefaceCache::Add(face, style);
    return face;
}

SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
    SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(path));
    return stream.get() ? CreateTypefaceFromStream(stream) : NULL;
}

///////////////////////////////////////////////////////////////////////////////

SkStream* FontConfigTypeface::onOpenStream(int* ttcIndex) const {
    SkStream* stream = this->getLocalStream();
    if (stream) {
        // TODO: fix issue 1176.
        // As of now open_stream will return a stream and unwind it, but the
        // SkStream is not thread safe, and if two threads use the stream they
        // may collide and print preview for example could still fail,
        // or there could be some failures in rendering if this stream is used
        // there.
        stream->rewind();
        stream->ref();
        // should have been provided by CreateFromStream()
        *ttcIndex = 0;
    } else {
        SkAutoTUnref<SkFontConfigInterface> fci(RefFCI());
        if (NULL == fci.get()) {
            return NULL;
        }
        stream = fci->openStream(this->getIdentity());
        *ttcIndex = this->getIdentity().fTTCIndex;
    }
    return stream;
}

int FontConfigTypeface::onGetTableTags(SkFontTableTag tags[]) const {
    int ttcIndex;
    SkAutoTUnref<SkStream> stream(this->openStream(&ttcIndex));
    return stream.get()
                ? SkFontStream::GetTableTags(stream, ttcIndex, tags)
                : 0;
}

size_t FontConfigTypeface::onGetTableData(SkFontTableTag tag, size_t offset,
                                  size_t length, void* data) const {
    int ttcIndex;
    SkAutoTUnref<SkStream> stream(this->openStream(&ttcIndex));
    return stream.get()
                ? SkFontStream::GetTableData(stream, ttcIndex,
                                             tag, offset, length, data)
                : 0;
}

void FontConfigTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
                                             bool* isLocalStream) const {
    desc->setFamilyName(this->getFamilyName());
    *isLocalStream = SkToBool(this->getLocalStream());
}

///////////////////////////////////////////////////////////////////////////////

// look for the last substring after a '/' and return that, or return null.
static const char* find_just_name(const char* str) {
    const char* last = strrchr(str, '/');
    return last ? last + 1 : NULL;
}

static bool is_lower(char c) {
    return c >= 'a' && c <= 'z';
}

#include "SkFontMgr.h"
#include <fontconfig/fontconfig.h>

static int get_int(FcPattern* pattern, const char field[]) {
    int value;
    if (FcPatternGetInteger(pattern, field, 0, &value) != FcResultMatch) {
        value = SK_MinS32;
    }
    return value;
}

static const char* get_name(FcPattern* pattern, const char field[]) {
    const char* name;
    if (FcPatternGetString(pattern, field, 0, (FcChar8**)&name) != FcResultMatch) {
        name = "";
    }
    return name;
}

static bool valid_pattern(FcPattern* pattern) {
    FcBool is_scalable;
    if (FcPatternGetBool(pattern, FC_SCALABLE, 0, &is_scalable) != FcResultMatch || !is_scalable) {
        return false;
    }

    // fontconfig can also return fonts which are unreadable
    const char* c_filename = get_name(pattern, FC_FILE);
    if (0 == *c_filename) {
        return false;
    }
    if (access(c_filename, R_OK) != 0) {
        return false;
    }
    return true;
}

static bool match_name(FcPattern* pattern, const char family_name[]) {
    return !strcasecmp(family_name, get_name(pattern, FC_FAMILY));
}

static FcPattern** MatchFont(FcFontSet* font_set,
                             const char post_config_family[],
                             int* count) {
  // Older versions of fontconfig have a bug where they cannot select
  // only scalable fonts so we have to manually filter the results.

    FcPattern** iter = font_set->fonts;
    FcPattern** stop = iter + font_set->nfont;
    // find the first good match
    for (; iter < stop; ++iter) {
        if (valid_pattern(*iter)) {
            break;
        }
    }

    if (iter == stop || !match_name(*iter, post_config_family)) {
        return NULL;
    }

    FcPattern** firstIter = iter++;
    for (; iter < stop; ++iter) {
        if (!valid_pattern(*iter) || !match_name(*iter, post_config_family)) {
            break;
        }
    }

    *count = iter - firstIter;
    return firstIter;
}

class SkFontStyleSet_FC : public SkFontStyleSet {
public:
    SkFontStyleSet_FC(FcPattern** matches, int count);
    virtual ~SkFontStyleSet_FC();

    virtual int count() SK_OVERRIDE { return fRecCount; }
    virtual void getStyle(int index, SkFontStyle*, SkString* style) SK_OVERRIDE;
    virtual SkTypeface* createTypeface(int index) SK_OVERRIDE;
    virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE;

private:
    struct Rec {
        SkString    fStyleName;
        SkString    fFileName;
        SkFontStyle fStyle;
    };
    Rec* fRecs;
    int  fRecCount;
};

static int map_range(int value,
                     int old_min, int old_max, int new_min, int new_max) {
    SkASSERT(old_min < old_max);
    SkASSERT(new_min < new_max);
    return new_min + SkMulDiv(value - old_min,
                              new_max - new_min, old_max - old_min);
}

static SkFontStyle make_fontconfig_style(FcPattern* match) {
    int weight = get_int(match, FC_WEIGHT);
    int width = get_int(match, FC_WIDTH);
    int slant = get_int(match, FC_SLANT);
//    SkDebugf("old weight %d new weight %d\n", weight, map_range(weight, 0, 80, 0, 400));

    // fontconfig weight seems to be 0..200 or so, so we remap it here
    weight = map_range(weight, 0, 80, 0, 400);
    width = map_range(width, 0, 200, 0, 9);
    return SkFontStyle(weight, width, slant > 0 ? SkFontStyle::kItalic_Slant
                                                : SkFontStyle::kUpright_Slant);
}

SkFontStyleSet_FC::SkFontStyleSet_FC(FcPattern** matches, int count) {
    fRecCount = count;
    fRecs = SkNEW_ARRAY(Rec, count);
    for (int i = 0; i < count; ++i) {
        fRecs[i].fStyleName.set(get_name(matches[i], FC_STYLE));
        fRecs[i].fFileName.set(get_name(matches[i], FC_FILE));
        fRecs[i].fStyle = make_fontconfig_style(matches[i]);

//        SkDebugf("%s [%d %d %d]\n", fRecs[i].fFileName.c_str(), fRecs[i].fStyle.weight(), fRecs[i].fStyle.width(), fRecs[i].fStyle.isItalic());
    }
}

SkFontStyleSet_FC::~SkFontStyleSet_FC() {
    SkDELETE_ARRAY(fRecs);
}

void SkFontStyleSet_FC::getStyle(int index, SkFontStyle* style,
                                 SkString* styleName) {
    SkASSERT((unsigned)index < (unsigned)fRecCount);
    if (style) {
        *style = fRecs[index].fStyle;
    }
    if (styleName) {
        *styleName = fRecs[index].fStyleName;
    }
}

SkTypeface* SkFontStyleSet_FC::createTypeface(int index) {
    return NULL;
}

SkTypeface* SkFontStyleSet_FC::matchStyle(const SkFontStyle& pattern) {
    return NULL;
}

static bool find_name(const SkTDArray<const char*>& array, const char* name) {
    for (int i = 0; i < array.count(); ++i) {
        if (0 == strcmp(array[i], name)) {
            return true;
        }
    }
    return false;
}

class SkFontMgr_fontconfig : public SkFontMgr {
    SkAutoTUnref<SkFontConfigInterface> fFCI;

    int fFamilyCount;
    SkString* fFamilyNames;

    void init() {
        if (fFamilyCount >= 0) {
            return;
        }

        FcPattern* pat = FcPatternCreate();
        FcObjectSet* os = FcObjectSetBuild (FC_FAMILY, (char *) 0);
        FcFontSet* fs = FcFontList(NULL, pat, os);

        SkTDArray<const char*> familyNames;
        familyNames.setReserve(fs->nfont);
        for (int i=0; fs && i < fs->nfont; ++i) {
            FcPattern* match = fs->fonts[i];
            const char* famName = get_name(match, FC_FAMILY);
            if (!find_name(familyNames, famName)) {
                *familyNames.append() = famName;
            }
//            SkDebugf("[%d %d] %s %s %x\n", i, fs->nfont, get_name(match, FC_FILE),
//                     get_name(match, FC_FAMILY), get_name(match, FC_FAMILY));
        }

        fFamilyCount = familyNames.count();
        fFamilyNames = SkNEW_ARRAY(SkString, fFamilyCount);
        for (int i = 0; i < fFamilyCount; ++i) {
            fFamilyNames[i].set(familyNames[i]);
        }

        if (fs) FcFontSetDestroy(fs);
        FcPatternDestroy(pat);
    }

public:
    SkFontMgr_fontconfig(SkFontConfigInterface* fci) : fFCI(fci) {
        fFamilyCount = -1;
    }

    virtual ~SkFontMgr_fontconfig() {
        SkDELETE_ARRAY(fFamilyNames);
    }

protected:
    virtual int onCountFamilies() { this->init(); return fFamilyCount; }

    virtual void onGetFamilyName(int index, SkString* familyName) {
        this->init();
        SkASSERT((unsigned)index < (unsigned)fFamilyCount);
        *familyName = fFamilyNames[index];
    }

    virtual SkFontStyleSet* onCreateStyleSet(int index) {
        this->init();
        SkASSERT((unsigned)index < (unsigned)fFamilyCount);
        return this->onMatchFamily(fFamilyNames[index].c_str());
    }

    virtual SkFontStyleSet* onMatchFamily(const char familyName[]) {
        this->init();

        FcPattern* pattern = FcPatternCreate();

        FcPatternAddString(pattern, FC_FAMILY, (FcChar8*)familyName);
#if 0
        FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
#endif
        FcConfigSubstitute(NULL, pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        const char* post_config_family = get_name(pattern, FC_FAMILY);

        FcResult result;
        FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
        if (!font_set) {
            FcPatternDestroy(pattern);
            return NULL;
        }

        int count;
        FcPattern** match = MatchFont(font_set, post_config_family, &count);
        if (!match) {
            FcPatternDestroy(pattern);
            FcFontSetDestroy(font_set);
            return NULL;
        }

        FcPatternDestroy(pattern);

        SkTDArray<FcPattern*> trimmedMatches;
        for (int i = 0; i < count; ++i) {
            const char* justName = find_just_name(get_name(match[i], FC_FILE));
            if (!is_lower(*justName)) {
                *trimmedMatches.append() = match[i];
            }
            #if 0
            printf("[%d:%d] %s %s [%d %d %d]\n", i, count,
                   get_name(match[i], FC_STYLE), get_name(match[i], FC_FILE),
                   get_int(match[i], FC_WEIGHT), get_int(match[i], FC_WIDTH),
                   get_int(match[i], FC_SLANT));
            #endif
        }

        SkFontStyleSet_FC* sset = SkNEW_ARGS(SkFontStyleSet_FC,
                                             (trimmedMatches.begin(),
                                              trimmedMatches.count()));
        return sset;
    }

    virtual SkTypeface* onMatchFamilyStyle(const char familyName[],
                                           const SkFontStyle&) { return NULL; }
    virtual SkTypeface* onMatchFaceStyle(const SkTypeface*,
                                         const SkFontStyle&) { return NULL; }

    virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) { return NULL; }
    virtual SkTypeface* onCreateFromStream(SkStream*, int ttcIndex) {
        return NULL;
    }
    virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) {
        return NULL;
    }
};

SkFontMgr* SkFontMgr::Factory() {
    SkFontConfigInterface* fci = RefFCI();
    return fci ? SkNEW_ARGS(SkFontMgr_fontconfig, (fci)) : NULL;
}

<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of meegotouch-controlpanelapplets.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/
#include "profilewidget.h"
#include "profiledatainterface.h"
#include "profiledialog.h"
#include "profileplugin.h"

#undef DEBUG
#include "../debug.h"

#include <DuiControlPanelIf>
#include <MLocale>
#include <MStatusIndicatorMenuExtensionInterface>

#include <QGraphicsLinearLayout>

static const char *profiles_translation = "profiles";

ProfileWidget::ProfileWidget (
    ProfilePlugin *profilePlugin,
    QGraphicsItem *parent) :
        MButton (parent),
        plugin (profilePlugin),
        dataIf (0)
{
    MLocale       locale;

    SYS_DEBUG ("");
    dataIf = new ProfileDataInterface ();

    // load our translation catalogue...
    locale.installTrCatalog (profiles_translation);
    MLocale::setDefault (locale);

    setViewType (MButton::iconType);
    setObjectName("StatusIndicatorMenuTopRowExtensionButton");
    connect(this, SIGNAL (clicked ()), this, SLOT (showProfileDialog ()));
    connect (dataIf, SIGNAL (currentProfile (int)), SLOT (profileChanged ()));

    profileChanged ();
}

ProfileWidget::~ProfileWidget ()
{
    SYS_DEBUG ("");

    delete dataIf;
    dataIf = 0;
}

void
ProfileWidget::profileChanged()
{
    //% "Profile"
    setText (qtTrId ("qtn_prof_profile"));

    setIconID(dataIf->mapId2StatusIconId(dataIf->getCurrentProfile()));
}

void ProfileWidget::showProfileDialog ()
{
    MStatusIndicatorMenuInterface *menu;

    menu = plugin->statusIndicatorMenuInterface ();
    if (menu) {
        menu->hideStatusIndicatorMenu ();
    }

    ProfileDialog dialog (dataIf);

    connect (&dialog, SIGNAL (profileChanged (int)), SLOT (profileChanged ()));

    dialog.exec ();
}

void
ProfileWidget::retranslateUi ()
{
    //% "Profile"
    setText (qtTrId ("qtn_prof_profile"));
}

<commit_msg>Changes: whitespace fixes...<commit_after>/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of meegotouch-controlpanelapplets.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/
#include "profilewidget.h"
#include "profiledatainterface.h"
#include "profiledialog.h"
#include "profileplugin.h"

#undef DEBUG
#include "../debug.h"

#include <DuiControlPanelIf>
#include <MLocale>
#include <MStatusIndicatorMenuExtensionInterface>

#include <QGraphicsLinearLayout>

static const char *profiles_translation = "profiles";

ProfileWidget::ProfileWidget (
    ProfilePlugin *profilePlugin,
    QGraphicsItem *parent) :
        MButton (parent),
        plugin (profilePlugin),
        dataIf (0)
{
    MLocale       locale;

    SYS_DEBUG ("");
    dataIf = new ProfileDataInterface ();

    // load our translation catalogue...
    locale.installTrCatalog (profiles_translation);
    MLocale::setDefault (locale);

    setViewType (MButton::iconType);
    setObjectName("StatusIndicatorMenuTopRowExtensionButton");
    connect (this, SIGNAL (clicked ()), this, SLOT (showProfileDialog ()));
    connect (dataIf, SIGNAL (currentProfile (int)), SLOT (profileChanged ()));

    profileChanged ();
}

ProfileWidget::~ProfileWidget ()
{
    SYS_DEBUG ("");

    delete dataIf;
    dataIf = 0;
}

void
ProfileWidget::profileChanged()
{
    //% "Profile"
    setText (qtTrId ("qtn_prof_profile"));

    setIconID(dataIf->mapId2StatusIconId(dataIf->getCurrentProfile()));
}

void
ProfileWidget::showProfileDialog ()
{
    MStatusIndicatorMenuInterface *menu;

    menu = plugin->statusIndicatorMenuInterface ();
    if (menu) {
        menu->hideStatusIndicatorMenu ();
    }

    ProfileDialog dialog (dataIf);

    connect (&dialog, SIGNAL (profileChanged (int)), SLOT (profileChanged ()));

    dialog.exec ();
}

void
ProfileWidget::retranslateUi ()
{
    //% "Profile"
    setText (qtTrId ("qtn_prof_profile"));
}

<|endoftext|>


<commit_before>#include <iostream>
#include <sstream>
#include <time.h>
#include <vector>
#include "chis_gomoku.h"
using namespace std;
using namespace chis;
int chis::gomocup() {
	string command;
	Board b;
	vector<int> cl;
	vector<Point> ps;

	bool last_is_board = false;
	while(1) {
		if(last_is_board && cl.back() == 2) {
			command = "BEGIN";
		}
		else {
			cin >> command;
			for(auto &i : command) {
				if(i >= 'a' && i <= 'z') {
					i += 'A' - 'a';
				}
			}
		}
		if(last_is_board) {
			b.clear();
			for(size_t i = 0; i < ps.size(); ++i) {
				b.move(ps[i].x, ps[i].y);
			}
			ps.clear();
			cl.clear();
			last_is_board = false;
		}
		if(command == "START") {
			b.clear();
			int size;
			cin >> size;
			if(size > 30 || size <= 5) {
				std::cout << "ERROR" << endl;
			}
			else {
				chis::SIZE = size;
				b.clear();
				std::cout << "OK" << endl;
			}
		}
		if(command == "RESTART") {
			b.clear();
			std::cout << "OK" << endl;
			//command = "BEGIN";
		}
		else if(command == "TAKEBACK") {
			int x, y;
			char dot;
			cin >> x >> dot >> y;
			if(x < 0 || x >= chis::SIZE || y < 0 || y >= chis::SIZE) {
				std::cout << "ERROR" << endl;
			}
			else {
				b.unmove();
				Point p;
#ifndef NEGAMAX
				p = chis_move(b);
#endif
#ifdef NEGAMAX
				p = chis_move(b);
#endif
				std::cout << (int)p.x << "," << (int)p.y << endl;
				b.move(p.x, p.y);
			}
		}
		else if(command == "BEGIN") {
			Point p;
#ifndef NEGAMAX
			p = chis_move(b);
#endif
#ifdef NEGAMAX
			p = chis_move(b);
#endif
			b.move(p.x, p.y);
			std::cout << (int)p.x << "," << (int)p.y << endl;
		}
		else if(command == "TURN") {
			int x, y;
			char dot;
			cin >> x >> dot >> y;
			if(x < 0 || x >= chis::SIZE || y < 0 || y >= chis::SIZE) {
				std::cout << "ERROR" << endl;
			}
			else {
				Point p;
				b.move(x, y);
#ifndef NEGAMAX
				p = chis_move(b);
#endif
#ifdef NEGAMAX
				p = chis_move(b);
#endif
				std::cout << (int)p.x << "," << (int)p.y << endl;
				b.move(p.x, p.y);
			}
		}

		else if(command == "BOARD") {
			cl.clear();
			ps.clear();
			b.clear();
			last_is_board = true;
			stringstream ss;
			cin >> command;
			while(command != "DONE") {
				ss.clear();
				ss << command;
				int x, y, c;
				char dot;
				ss >> x >> dot >> y >> dot >> c;
				if(x < 0 || x >= chis::SIZE || y < 0 || y >= chis::SIZE || (c != 1 && c != 2)) {
					std::cout << "ERROR" << endl;
				}
				else {
					ps.push_back(Point(x, y));
					cl.push_back(c);
				}
				cin >> command;
			}

		}
		else if(command == "INFO") {
			string key;
			cin >> key;
			if(key == "timeout_turn") {//ֻ
				int value;
				cin >> value;
				if(value >= 100) {
					SEARCH_TIME = value - 170;
				}
				else {
					SEARCH_TIME = 100;
				}
				CHIS_CONFIG.SEARCH_TIME = SEARCH_TIME;
			}
			else if(key == "timeout_match") {
				cin >> chis::timeout_match;
			}
			else if(key == "max_memory") {
				int value;
				cin >> value;
				//TODO
			}
			else if(key == "time_left") {
				cin >> time_left;
			}
			else if(key == "game_type") {
				int value;
				cin >> value;
				//TODO
			}
			else if(key == "rule") {
				int value;
				cin >> value;
				//TODO
			}
			else if(key == "folder") {
				string t;
				cin >> t;
			}
		}
		else if(command == "END") {
			b.clear();
		}

	}

}<commit_msg>Update gomocup.cpp<commit_after>#include <iostream>
#include <sstream>
#include <time.h>
#include <vector>
#include "chis_gomoku.h"
using namespace std;
using namespace chis;
int chis::gomocup() {
	string command;
	Board b;
	vector<int> cl;
	vector<Point> ps;

	bool last_is_board = false;
	while(1) {
		if(last_is_board && cl.back() == 2) {
			command = "BEGIN";
		}
		else {
			cin >> command;
			for(auto &i : command) {
				if(i >= 'a' && i <= 'z') {
					i += 'A' - 'a';
				}
			}
		}
		if(last_is_board) {
			b.clear();
			for(size_t i = 0; i < ps.size(); ++i) {
				b.move(ps[i].x, ps[i].y);
			}
			ps.clear();
			cl.clear();
			last_is_board = false;
		}
		if(command == "START") {
			b.clear();
			int size;
			cin >> size;
			if(size > 30 || size <= 5) {
				std::cout << "ERROR" << endl;
			}
			else {
				chis::SIZE = size;
				b.clear();
				std::cout << "OK" << endl;
			}
		}
		if(command == "RESTART") {
			b.clear();
			std::cout << "OK" << endl;
			//command = "BEGIN";
		}
		else if(command == "TAKEBACK") {
			int x, y;
			char dot;
			cin >> x >> dot >> y;
			if(x < 0 || x >= chis::SIZE || y < 0 || y >= chis::SIZE) {
				std::cout << "ERROR" << endl;
			}
			else {
				b.unmove();
				Point p;
				p = chis_move(b);
				std::cout << (int)p.x << "," << (int)p.y << endl;
				b.move(p.x, p.y);
			}
		}
		else if(command == "BEGIN") {
			Point p;
			p = chis_move(b);
			b.move(p.x, p.y);
			std::cout << (int)p.x << "," << (int)p.y << endl;
		}
		else if(command == "TURN") {
			int x, y;
			char dot;
			cin >> x >> dot >> y;
			if(x < 0 || x >= chis::SIZE || y < 0 || y >= chis::SIZE) {
				std::cout << "ERROR" << endl;
			}
			else {
				Point p;
				b.move(x, y);
				p = chis_move(b);
				std::cout << (int)p.x << "," << (int)p.y << endl;
				b.move(p.x, p.y);
			}
		}

		else if(command == "BOARD") {
			cl.clear();
			ps.clear();
			b.clear();
			last_is_board = true;
			stringstream ss;
			cin >> command;
			while(command != "DONE") {
				ss.clear();
				ss << command;
				int x, y, c;
				char dot;
				ss >> x >> dot >> y >> dot >> c;
				if(x < 0 || x >= chis::SIZE || y < 0 || y >= chis::SIZE || (c != 1 && c != 2)) {
					std::cout << "ERROR" << endl;
				}
				else {
					ps.push_back(Point(x, y));
					cl.push_back(c);
				}
				cin >> command;
			}

		}
		else if(command == "INFO") {
			string key;
			cin >> key;
			if(key == "timeout_turn") {//Ö»½ÓÊÜÂýÆå
				int value;
				cin >> value;
				if(value >= 100) {
					SEARCH_TIME = value - 170;
				}
				else {
					SEARCH_TIME = 100;
				}
				CHIS_CONFIG.SEARCH_TIME = SEARCH_TIME;
			}
			else if(key == "timeout_match") {
				cin >> chis::timeout_match;
			}
			else if(key == "max_memory") {
				int value;
				cin >> value;
				//TODO
			}
			else if(key == "time_left") {
				cin >> time_left;
			}
			else if(key == "game_type") {
				int value;
				cin >> value;
				//TODO
			}
			else if(key == "rule") {
				int value;
				cin >> value;
				//TODO
			}
			else if(key == "folder") {
				string t;
				cin >> t;
			}
		}
		else if(command == "END") {
			b.clear();
		}

	}

}
<|endoftext|>


<commit_before><commit_msg>Fixing linker errors in file.<commit_after><|endoftext|>


<commit_before>#include <bts/blockchain/chain_interface.hpp>
#include <bts/blockchain/exceptions.hpp>
#include <bts/blockchain/market_operations.hpp>

namespace bts { namespace blockchain {

   /**
    *  If the amount is negative then it will withdraw/cancel the bid assuming
    *  it is signed by the owner and there is sufficient funds.
    *
    *  If the amount is positive then it will add funds to the bid.
    */
   void bid_operation::evaluate( transaction_evaluation_state& eval_state )
   { try {
      if( this->bid_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (bid_index.order_price) );

      auto owner = this->bid_index.owner;
      if( !eval_state.check_signature( owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (bid_index.owner) );

      asset delta_amount  = this->get_amount();

      eval_state.validate_asset( delta_amount );

      auto current_bid   = eval_state._current_state->get_bid_record( this->bid_index );

      if( this->amount == 0 ) FC_CAPTURE_AND_THROW( zero_amount );
      if( this->amount <  0 ) // withdraw
      {
          if( NOT current_bid )
             FC_CAPTURE_AND_THROW( unknown_market_order, (bid_index) );

          if( llabs(this->amount) > current_bid->balance )
             FC_CAPTURE_AND_THROW( insufficient_funds, (amount)(current_bid->balance) );

          // add the delta amount to the eval state that we withdrew from the bid
          eval_state.add_balance( -delta_amount );
      }
      else // this->amount > 0 - deposit
      {
          if( NOT current_bid )  // then initialize to 0
            current_bid = order_record();
          // sub the delta amount from the eval state that we deposited to the bid
          eval_state.sub_balance( balance_id_type(), delta_amount );
      }

      current_bid->balance     += this->amount;

      // bids do not count toward depth... they can set any price they like and create arbitrary depth
      //auto market_stat = eval_state._current_state->get_market_status( bid_index.order_price.quote_asset_id, bid_index.order_price.base_asset_id );
      //if( !market_stat )
      //   market_stat = market_status(0,0);
      // market_stat->bid_depth += (delta_amount * bid_index.order_price).amount;
      //eval_state._current_state->store_market_status( *market_stat );

      eval_state._current_state->store_bid_record( this->bid_index, *current_bid );

      //auto check   = eval_state._current_state->get_bid_record( this->bid_index );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }

   /**
    *  If the amount is negative then it will withdraw/cancel the bid assuming
    *  it is signed by the owner and there is sufficient funds.
    *
    *  If the amount is positive then it will add funds to the bid.
    */
   void ask_operation::evaluate( transaction_evaluation_state& eval_state )
   { try {
      if( this->ask_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (ask_index.order_price) );

      auto owner = this->ask_index.owner;
      if( !eval_state.check_signature( owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (ask_index.owner) );

      asset delta_amount  = this->get_amount();

      eval_state.validate_asset( delta_amount );

      auto current_ask   = eval_state._current_state->get_ask_record( this->ask_index );

      if( this->amount == 0 ) FC_CAPTURE_AND_THROW( zero_amount );
      if( this->amount <  0 ) // withdraw
      {
          if( NOT current_ask )
             FC_CAPTURE_AND_THROW( unknown_market_order, (ask_index) );

          if( llabs(this->amount) > current_ask->balance )
             FC_CAPTURE_AND_THROW( insufficient_funds, (amount)(current_ask->balance) );

          // add the delta amount to the eval state that we withdrew from the ask
          eval_state.add_balance( -delta_amount );
      }
      else // this->amount > 0 - deposit
      {
          if( NOT current_ask )  // then initialize to 0
            current_ask = order_record();
          // sub the delta amount from the eval state that we deposited to the ask
          eval_state.sub_balance( balance_id_type(), delta_amount );
      }

      current_ask->balance     += this->amount;
      FC_ASSERT( current_ask->balance >= 0, "", ("current_ask",current_ask)  );

      auto market_stat = eval_state._current_state->get_market_status( ask_index.order_price.quote_asset_id, ask_index.order_price.base_asset_id );

      if( !market_stat )
         market_stat = market_status(ask_index.order_price.quote_asset_id, ask_index.order_price.base_asset_id, 0,0);
      market_stat->ask_depth += delta_amount.amount;

      eval_state._current_state->store_market_status( *market_stat );

      eval_state._current_state->store_ask_record( this->ask_index, *current_ask );

      //auto check   = eval_state._current_state->get_ask_record( this->ask_index );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }

   void short_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      if( this->short_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (short_index.order_price) );

      auto owner = this->short_index.owner;
      if( !eval_state.check_signature( owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (short_index.owner) );

      asset delta_amount  = this->get_amount();
      asset delta_quote   = delta_amount * this->short_index.order_price;

      /** if the USD amount of the order is effectively then don't bother */
      FC_ASSERT( llabs(delta_quote.amount) > 0, "", ("delta_quote",delta_quote)("order",*this));

      eval_state.validate_asset( delta_amount );
      auto  asset_to_short = eval_state._current_state->get_asset_record( short_index.order_price.quote_asset_id );
      FC_ASSERT( asset_to_short.valid() );
      FC_ASSERT( asset_to_short->is_market_issued(), "${symbol} is not a market issued asset", ("symbol",asset_to_short->symbol) );

      auto current_short   = eval_state._current_state->get_short_record( this->short_index );
      //if( current_short ) wdump( (current_short) );

      if( this->amount == 0 ) FC_CAPTURE_AND_THROW( zero_amount );
      if( this->amount <  0 ) // withdraw
      {
          if( NOT current_short )
             FC_CAPTURE_AND_THROW( unknown_market_order, (short_index) );

          if( llabs(this->amount) > current_short->balance )
             FC_CAPTURE_AND_THROW( insufficient_funds, (amount)(current_short->balance) );

          // add the delta amount to the eval state that we withdrew from the short
          eval_state.add_balance( -delta_amount );
      }
      else // this->amount > 0 - deposit
      {
          if( NOT current_short )  // then initialize to 0
            current_short = order_record();
          // sub the delta amount from the eval state that we deposited to the short
          eval_state.sub_balance( balance_id_type(), delta_amount );
      }

      current_short->balance     += this->amount;
      FC_ASSERT( current_short->balance >= 0 );

      auto market_stat = eval_state._current_state->get_market_status( short_index.order_price.quote_asset_id, short_index.order_price.base_asset_id );
      if( !market_stat )
         market_stat = market_status(short_index.order_price.quote_asset_id, short_index.order_price.base_asset_id, 0,0);
      market_stat->bid_depth += delta_amount.amount;

      eval_state._current_state->store_market_status( *market_stat );

      eval_state._current_state->store_short_record( this->short_index, *current_short );
   }

   /**
     pay off part of the USD balance, if balance goes to 0 then close out
     the position and transfer collateral to proper place.
     update the call price (remove old value, add new value)
   */
   void cover_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      if( this->cover_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (cover_index.order_price) );

      if( this->amount == 0 && !this->new_cover_price )
         FC_CAPTURE_AND_THROW( zero_amount );

      if( this->amount < 0 )
         FC_CAPTURE_AND_THROW( negative_deposit );

      asset delta_amount  = this->get_amount();

      if( !eval_state.check_signature( cover_index.owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (cover_index.owner) );

      // subtract this from the transaction
      eval_state.sub_balance( address(), delta_amount );

      auto current_cover   = eval_state._current_state->get_collateral_record( this->cover_index );
      if( NOT current_cover )
         FC_CAPTURE_AND_THROW( unknown_market_order, (cover_index) );

      current_cover->payoff_balance -= delta_amount.amount;
      // changing the payoff balance changes the call price... so we need to remove the old record
      // and insert a new one.
      eval_state._current_state->store_collateral_record( this->cover_index, collateral_record() );

      if( current_cover->payoff_balance > 0 )
      {
         auto new_call_price = asset(current_cover->payoff_balance, delta_amount.asset_id) /
                               asset((current_cover->collateral_balance*3)/4, 0);

         if( this->new_cover_price && (*this->new_cover_price > new_call_price) )
            eval_state._current_state->store_collateral_record( market_index_key( *this->new_cover_price, this->cover_index.owner),
                                                                *current_cover );
         else
            eval_state._current_state->store_collateral_record( market_index_key( new_call_price, this->cover_index.owner),
                                                                *current_cover );
      }
      else // withdraw the collateral to the transaction to be deposited at owners discretion / cover fees
      {
         eval_state.add_balance( asset( current_cover->collateral_balance, 0 ) );

         auto market_stat = eval_state._current_state->get_market_status( cover_index.order_price.quote_asset_id, cover_index.order_price.base_asset_id );
         FC_ASSERT( market_stat, "this should be valid for there to even be a position to cover" );
         market_stat->ask_depth -= current_cover->collateral_balance;

         eval_state._current_state->store_market_status( *market_stat );
      }
   }

   void add_collateral_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      if( this->cover_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (cover_index.order_price) );

      if( this->amount == 0 )
         FC_CAPTURE_AND_THROW( zero_amount );

      if( this->amount < 0 )
         FC_CAPTURE_AND_THROW( negative_deposit );

      asset delta_amount  = this->get_amount();
      eval_state.sub_balance( address(), delta_amount );

      // update collateral and call price
      auto current_cover   = eval_state._current_state->get_collateral_record( this->cover_index );
      if( NOT current_cover )
         FC_CAPTURE_AND_THROW( unknown_market_order, (cover_index) );

      current_cover->collateral_balance += delta_amount.amount;

      // changing the payoff balance changes the call price... so we need to remove the old record
      // and insert a new one.
      eval_state._current_state->store_collateral_record( this->cover_index, collateral_record() );

      auto new_call_price = asset(current_cover->payoff_balance, delta_amount.asset_id) /
                            asset((current_cover->collateral_balance*3)/4, 0);

      eval_state._current_state->store_collateral_record( market_index_key( new_call_price, this->cover_index.owner),
                                                          *current_cover );

      auto market_stat = eval_state._current_state->get_market_status( cover_index.order_price.quote_asset_id, cover_index.order_price.base_asset_id );
      FC_ASSERT( market_stat, "this should be valid for there to even be a position to cover" );
      market_stat->ask_depth += delta_amount.amount;

      eval_state._current_state->store_market_status( *market_stat );
   }

   void remove_collateral_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      // Should this even be allowed?
   }

} } // bts::blockchain
<commit_msg>restrict new shorts to within 10 percent<commit_after>#include <bts/blockchain/chain_interface.hpp>
#include <bts/blockchain/exceptions.hpp>
#include <bts/blockchain/market_operations.hpp>

namespace bts { namespace blockchain {

   /**
    *  If the amount is negative then it will withdraw/cancel the bid assuming
    *  it is signed by the owner and there is sufficient funds.
    *
    *  If the amount is positive then it will add funds to the bid.
    */
   void bid_operation::evaluate( transaction_evaluation_state& eval_state )
   { try {
      if( this->bid_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (bid_index.order_price) );

      auto owner = this->bid_index.owner;
      if( !eval_state.check_signature( owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (bid_index.owner) );

      asset delta_amount  = this->get_amount();

      eval_state.validate_asset( delta_amount );

      auto current_bid   = eval_state._current_state->get_bid_record( this->bid_index );

      if( this->amount == 0 ) FC_CAPTURE_AND_THROW( zero_amount );
      if( this->amount <  0 ) // withdraw
      {
          if( NOT current_bid )
             FC_CAPTURE_AND_THROW( unknown_market_order, (bid_index) );

          if( llabs(this->amount) > current_bid->balance )
             FC_CAPTURE_AND_THROW( insufficient_funds, (amount)(current_bid->balance) );

          // add the delta amount to the eval state that we withdrew from the bid
          eval_state.add_balance( -delta_amount );
      }
      else // this->amount > 0 - deposit
      {
          if( NOT current_bid )  // then initialize to 0
            current_bid = order_record();
          // sub the delta amount from the eval state that we deposited to the bid
          eval_state.sub_balance( balance_id_type(), delta_amount );
      }

      current_bid->balance     += this->amount;

      // bids do not count toward depth... they can set any price they like and create arbitrary depth
      //auto market_stat = eval_state._current_state->get_market_status( bid_index.order_price.quote_asset_id, bid_index.order_price.base_asset_id );
      //if( !market_stat )
      //   market_stat = market_status(0,0);
      // market_stat->bid_depth += (delta_amount * bid_index.order_price).amount;
      //eval_state._current_state->store_market_status( *market_stat );

      eval_state._current_state->store_bid_record( this->bid_index, *current_bid );

      //auto check   = eval_state._current_state->get_bid_record( this->bid_index );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }

   /**
    *  If the amount is negative then it will withdraw/cancel the bid assuming
    *  it is signed by the owner and there is sufficient funds.
    *
    *  If the amount is positive then it will add funds to the bid.
    */
   void ask_operation::evaluate( transaction_evaluation_state& eval_state )
   { try {
      if( this->ask_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (ask_index.order_price) );

      auto owner = this->ask_index.owner;
      if( !eval_state.check_signature( owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (ask_index.owner) );

      asset delta_amount  = this->get_amount();

      eval_state.validate_asset( delta_amount );

      auto current_ask   = eval_state._current_state->get_ask_record( this->ask_index );

      if( this->amount == 0 ) FC_CAPTURE_AND_THROW( zero_amount );
      if( this->amount <  0 ) // withdraw
      {
          if( NOT current_ask )
             FC_CAPTURE_AND_THROW( unknown_market_order, (ask_index) );

          if( llabs(this->amount) > current_ask->balance )
             FC_CAPTURE_AND_THROW( insufficient_funds, (amount)(current_ask->balance) );

          // add the delta amount to the eval state that we withdrew from the ask
          eval_state.add_balance( -delta_amount );
      }
      else // this->amount > 0 - deposit
      {
          if( NOT current_ask )  // then initialize to 0
            current_ask = order_record();
          // sub the delta amount from the eval state that we deposited to the ask
          eval_state.sub_balance( balance_id_type(), delta_amount );
      }

      current_ask->balance     += this->amount;
      FC_ASSERT( current_ask->balance >= 0, "", ("current_ask",current_ask)  );

      auto market_stat = eval_state._current_state->get_market_status( ask_index.order_price.quote_asset_id, ask_index.order_price.base_asset_id );

      if( !market_stat )
         market_stat = market_status(ask_index.order_price.quote_asset_id, ask_index.order_price.base_asset_id, 0,0);
      market_stat->ask_depth += delta_amount.amount;

      eval_state._current_state->store_market_status( *market_stat );

      eval_state._current_state->store_ask_record( this->ask_index, *current_ask );

      //auto check   = eval_state._current_state->get_ask_record( this->ask_index );
   } FC_CAPTURE_AND_RETHROW( (*this) ) }

   void short_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      if( this->short_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (short_index.order_price) );

      auto owner = this->short_index.owner;
      if( !eval_state.check_signature( owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (short_index.owner) );

      asset delta_amount  = this->get_amount();
      asset delta_quote   = delta_amount * this->short_index.order_price;

      /** if the USD amount of the order is effectively then don't bother */
      FC_ASSERT( llabs(delta_quote.amount) > 0, "", ("delta_quote",delta_quote)("order",*this));

      eval_state.validate_asset( delta_amount );
      auto  asset_to_short = eval_state._current_state->get_asset_record( short_index.order_price.quote_asset_id );
      FC_ASSERT( asset_to_short.valid() );
      FC_ASSERT( asset_to_short->is_market_issued(), "${symbol} is not a market issued asset", ("symbol",asset_to_short->symbol) );

      auto current_short   = eval_state._current_state->get_short_record( this->short_index );
      //if( current_short ) wdump( (current_short) );

      if( this->amount == 0 ) FC_CAPTURE_AND_THROW( zero_amount );
      if( this->amount <  0 ) // withdraw
      {
          if( NOT current_short )
             FC_CAPTURE_AND_THROW( unknown_market_order, (short_index) );

          if( llabs(this->amount) > current_short->balance )
             FC_CAPTURE_AND_THROW( insufficient_funds, (amount)(current_short->balance) );

          // add the delta amount to the eval state that we withdrew from the short
          eval_state.add_balance( -delta_amount );
      }
      else // this->amount > 0 - deposit
      {
          if( NOT current_short )  // then initialize to 0
            current_short = order_record();
          // sub the delta amount from the eval state that we deposited to the short
          eval_state.sub_balance( balance_id_type(), delta_amount );
      }

      current_short->balance     += this->amount;
      FC_ASSERT( current_short->balance >= 0 );

      auto market_stat = eval_state._current_state->get_market_status( short_index.order_price.quote_asset_id, short_index.order_price.base_asset_id );
      if( !market_stat )
         market_stat = market_status(short_index.order_price.quote_asset_id, short_index.order_price.base_asset_id, 0,0);

      /**
       *  If there is an average, then keep it within 10% of the average.
       */
      if( market_stat->avg_price_1h.quote_asset_id != 0 )
      {
         FC_ASSERT( short_index.order_price < market_stat->maximum_bid(), "", ("order",*this)("market_stat",market_stat) );
      }
      else // if there is no average, there must be a median feed and the short must not be more than 10% above the feed
      {
         auto median_delegate_price = eval_state._current_state->get_median_delegate_price( short_index.order_price.quote_asset_id );
         FC_ASSERT( median_delegate_price.valid() );
         auto feed_max_short_bid = *median_delegate_price;
         feed_max_short_bid.ratio *= 10;
         feed_max_short_bid.ratio /= 9;
         FC_ASSERT( short_index.order_price < feed_max_short_bid, "", ("order",*this)("max_short_price",feed_max_short_bid) );
      }

      market_stat->bid_depth += delta_amount.amount;

      eval_state._current_state->store_market_status( *market_stat );

      eval_state._current_state->store_short_record( this->short_index, *current_short );
   }

   /**
     pay off part of the USD balance, if balance goes to 0 then close out
     the position and transfer collateral to proper place.
     update the call price (remove old value, add new value)
   */
   void cover_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      if( this->cover_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (cover_index.order_price) );

      if( this->amount == 0 && !this->new_cover_price )
         FC_CAPTURE_AND_THROW( zero_amount );

      if( this->amount < 0 )
         FC_CAPTURE_AND_THROW( negative_deposit );

      asset delta_amount  = this->get_amount();

      if( !eval_state.check_signature( cover_index.owner ) )
         FC_CAPTURE_AND_THROW( missing_signature, (cover_index.owner) );

      // subtract this from the transaction
      eval_state.sub_balance( address(), delta_amount );

      auto current_cover   = eval_state._current_state->get_collateral_record( this->cover_index );
      if( NOT current_cover )
         FC_CAPTURE_AND_THROW( unknown_market_order, (cover_index) );

      current_cover->payoff_balance -= delta_amount.amount;
      // changing the payoff balance changes the call price... so we need to remove the old record
      // and insert a new one.
      eval_state._current_state->store_collateral_record( this->cover_index, collateral_record() );

      if( current_cover->payoff_balance > 0 )
      {
         auto new_call_price = asset(current_cover->payoff_balance, delta_amount.asset_id) /
                               asset((current_cover->collateral_balance*3)/4, 0);

         if( this->new_cover_price && (*this->new_cover_price > new_call_price) )
            eval_state._current_state->store_collateral_record( market_index_key( *this->new_cover_price, this->cover_index.owner),
                                                                *current_cover );
         else
            eval_state._current_state->store_collateral_record( market_index_key( new_call_price, this->cover_index.owner),
                                                                *current_cover );
      }
      else // withdraw the collateral to the transaction to be deposited at owners discretion / cover fees
      {
         eval_state.add_balance( asset( current_cover->collateral_balance, 0 ) );

         auto market_stat = eval_state._current_state->get_market_status( cover_index.order_price.quote_asset_id, cover_index.order_price.base_asset_id );
         FC_ASSERT( market_stat, "this should be valid for there to even be a position to cover" );
         market_stat->ask_depth -= current_cover->collateral_balance;

         eval_state._current_state->store_market_status( *market_stat );
      }
   }

   void add_collateral_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      if( this->cover_index.order_price == price() )
         FC_CAPTURE_AND_THROW( zero_price, (cover_index.order_price) );

      if( this->amount == 0 )
         FC_CAPTURE_AND_THROW( zero_amount );

      if( this->amount < 0 )
         FC_CAPTURE_AND_THROW( negative_deposit );

      asset delta_amount  = this->get_amount();
      eval_state.sub_balance( address(), delta_amount );

      // update collateral and call price
      auto current_cover   = eval_state._current_state->get_collateral_record( this->cover_index );
      if( NOT current_cover )
         FC_CAPTURE_AND_THROW( unknown_market_order, (cover_index) );

      current_cover->collateral_balance += delta_amount.amount;

      // changing the payoff balance changes the call price... so we need to remove the old record
      // and insert a new one.
      eval_state._current_state->store_collateral_record( this->cover_index, collateral_record() );

      auto new_call_price = asset(current_cover->payoff_balance, delta_amount.asset_id) /
                            asset((current_cover->collateral_balance*3)/4, 0);

      eval_state._current_state->store_collateral_record( market_index_key( new_call_price, this->cover_index.owner),
                                                          *current_cover );

      auto market_stat = eval_state._current_state->get_market_status( cover_index.order_price.quote_asset_id, cover_index.order_price.base_asset_id );
      FC_ASSERT( market_stat, "this should be valid for there to even be a position to cover" );
      market_stat->ask_depth += delta_amount.amount;

      eval_state._current_state->store_market_status( *market_stat );
   }

   void remove_collateral_operation::evaluate( transaction_evaluation_state& eval_state )
   {
      // Should this even be allowed?
   }

} } // bts::blockchain
<|endoftext|>


<commit_before>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2021, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#if defined(__GNUC__)  // Needed for ffmpeg headers. Only allowed here when not
// using precomp. headers
#define __STDC_CONSTANT_MACROS	// Needed for having "UINT64_C" and so
#endif
//
#include "hwdrivers-precomp.h"	// Precompiled headers
//
#include <mrpt/config.h>

#if MRPT_HAS_FFMPEG
extern "C"
{
#define _MSC_STDINT_H_	// We already have pstdint.h in MRPT
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#endif

#include <mrpt/hwdrivers/CFFMPEG_InputStream.h>

using namespace mrpt;
using namespace mrpt::hwdrivers;

// JLBC: This file takes portions of code from the example
// "avcodec_sample.0.4.9.cpp"
//
// Minimum ffmpeg libs versions we want to support:
// Ubuntu 16.04 LTS: avcodec 56.60.100, avutil 54.31.100, avformat 56.40.101
// Ubuntu 20.04 LTS: avcodec 58.54.100, avutil 56.31.100, avformat 58.29.100
//
#if MRPT_HAS_FFMPEG
namespace mrpt::hwdrivers
{
// All context for ffmpeg:
struct TFFMPEGContext
{
	AVFormatContext* pFormatCtx{nullptr};
	int videoStream{0};
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	AVCodecParameters* pCodecPars{nullptr};
#endif
	AVCodec* pCodec{nullptr};
	AVCodecContext* pCodecCtx{nullptr};
	AVFrame* pFrame{nullptr};
	AVFrame* pFrameRGB{nullptr};
	SwsContext* img_convert_ctx{nullptr};
	std::vector<uint8_t> buffer;
};
}  // namespace mrpt::hwdrivers
#endif

struct CFFMPEG_InputStream::Impl
{
#if MRPT_HAS_FFMPEG
	TFFMPEGContext m_state;
#endif
};

/* --------------------------------------------------------
					Ctor
   -------------------------------------------------------- */
CFFMPEG_InputStream::CFFMPEG_InputStream()
#if MRPT_HAS_FFMPEG
	: m_impl(mrpt::make_impl<CFFMPEG_InputStream::Impl>())
{
// av_register_all() not needed in ffmpeg >=4.0
#if LIBAVFORMAT_VERSION_MAJOR < 58
	// Register all formats and codecs
	av_register_all();
#endif
}
#else
{
	THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.");
}
#endif

/* --------------------------------------------------------
					Dtor
   -------------------------------------------------------- */
CFFMPEG_InputStream::~CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	// Close everything:
	this->close();
#endif
}

/* --------------------------------------------------------
					isOpen
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::isOpen() const
{
#if MRPT_HAS_FFMPEG
	const TFFMPEGContext* ctx = &m_impl->m_state;
	return ctx->pFormatCtx != nullptr;
#else
	return false;
#endif
}

/* --------------------------------------------------------
					openURL
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::openURL(
	const std::string& url, bool grab_as_grayscale, bool verbose,
	const std::map<std::string, std::string>& optionsMap)
{
#if MRPT_HAS_FFMPEG
	this->close();	// Close first

	TFFMPEGContext* ctx = &m_impl->m_state;

	this->m_url = url;
	this->m_grab_as_grayscale = grab_as_grayscale;

	AVDictionary* options = nullptr;  // "create" an empty dictionary
	for (const auto& kv : optionsMap)
		av_dict_set(&options, kv.first.c_str(), kv.second.c_str(), 0);

	// Open video file
	if (avformat_open_input(&ctx->pFormatCtx, url.c_str(), nullptr, &options) !=
		0)
	{
		ctx->pFormatCtx = nullptr;
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url
				  << std::endl;
		return false;
	}

	// Retrieve stream information
	if (avformat_find_stream_info(ctx->pFormatCtx, nullptr) < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream "
					 "information: "
				  << url << std::endl;
		return false;
	}

	// Dump information about file onto standard error
	if (verbose) { av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false); }

	// Find the first video stream
	ctx->videoStream = -1;
	for (unsigned int i = 0; i < ctx->pFormatCtx->nb_streams; i++)
	{
#if LIBAVFORMAT_VERSION_MAJOR >= 57
		auto codecType = ctx->pFormatCtx->streams[i]->codecpar->codec_type;
#else
		auto codecType = ctx->pFormatCtx->streams[i]->codec->codec_type;
#endif
		if (codecType == AVMEDIA_TYPE_VIDEO)
		{
			ctx->videoStream = (int)i;
			break;
		}
	}
	if (ctx->videoStream == -1)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] Didn't find a video stream: "
			<< url << std::endl;
		return false;
	}

	// Get a pointer to the codec context for the video stream
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	ctx->pCodecPars = ctx->pFormatCtx->streams[ctx->videoStream]->codecpar;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecPars->codec_id);
#else
	ctx->pCodecCtx = ctx->pFormatCtx->streams[ctx->videoStream]->codec;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecCtx->codec_id);
#endif
	if (ctx->pCodec == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url
				  << std::endl;
		return false;
	}

#if LIBAVFORMAT_VERSION_MAJOR >= 57
	ctx->pCodecCtx = avcodec_alloc_context3(nullptr /*ctx->pCodec*/);
	if (!ctx->pCodecCtx)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot alloc avcodec "
					 "context for: "
				  << url << std::endl;
		return false;
	}

	// Add stream parameters to context
	if (avcodec_parameters_to_context(
			ctx->pCodecCtx,
			ctx->pFormatCtx->streams[ctx->videoStream]->codecpar))
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Failed "
					 "avcodec_parameters_to_context() for: "
				  << url << std::endl;
		return false;
	}

	// Make sure that Codecs are identical or  avcodec_open2 fails.
	ctx->pCodecCtx->codec_id = ctx->pCodec->id;
#endif

	// Open codec
	if (avcodec_open2(ctx->pCodecCtx, ctx->pCodec, nullptr) < 0)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] avcodec_open2() failed for: "
			<< url << std::endl;
		return false;
	}

	// Allocate video frame
	ctx->pFrame = av_frame_alloc();
	// Allocate an AVFrame structure
	ctx->pFrameRGB = av_frame_alloc();

	if (ctx->pFrameRGB == nullptr || ctx->pFrame == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory "
					 "for frame buffers: "
				  << url << std::endl;
		return false;
	}

	// Determine required buffer size and allocate buffer
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif
	int numBytes = av_image_get_buffer_size(
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);
	if (numBytes < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] av_image_get_buffer_size "
					 "error code: "
				  << numBytes << std::endl;
		return false;
	}

	ctx->buffer.resize(numBytes);

	// Assign appropriate parts of buffer to image planes in pFrameRGB

	av_image_fill_arrays(
		ctx->pFrameRGB->data, ctx->pFrameRGB->linesize, &ctx->buffer[0],
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);

	return true;  // OK.
#else
	return false;
#endif
}

/* --------------------------------------------------------
					close
   -------------------------------------------------------- */
void CFFMPEG_InputStream::close()
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return;

	TFFMPEGContext* ctx = &m_impl->m_state;

	// Close the codec
	if (ctx->pCodecCtx)
	{
		avcodec_close(ctx->pCodecCtx);
		ctx->pCodecCtx = nullptr;
	}

	// Close the video file
	if (ctx->pFormatCtx)
	{
		avformat_close_input(&ctx->pFormatCtx);
		ctx->pFormatCtx = nullptr;
	}

	// Free frames memory:
	ctx->buffer.clear();

	if (ctx->pFrameRGB)
	{
		av_frame_free(&ctx->pFrameRGB);
		ctx->pFrameRGB = nullptr;
	}
	if (ctx->pFrame)
	{
		av_frame_free(&ctx->pFrame);
		ctx->pFrame = nullptr;
	}

	if (ctx->img_convert_ctx)
	{
		sws_freeContext(ctx->img_convert_ctx);
		ctx->img_convert_ctx = nullptr;
	}

#endif
}

/* --------------------------------------------------------
					retrieveFrame
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::retrieveFrame(mrpt::img::CImage& out_img)
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return false;

	TFFMPEGContext* ctx = &m_impl->m_state;

	AVPacket packet;

#if LIBAVFORMAT_VERSION_MAJOR < 57
	int frameFinished;
#endif

#if LIBAVFORMAT_VERSION_MAJOR >= 57
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif

	while (av_read_frame(ctx->pFormatCtx, &packet) >= 0)
	{
		// Is this a packet from the video stream?
		if (packet.stream_index != ctx->videoStream)
		{
			av_packet_unref(&packet);
			continue;
		}

		// Decode video frame
#if LIBAVFORMAT_VERSION_MAJOR >= 57
		int ret = avcodec_send_packet(ctx->pCodecCtx, &packet);
		if (ret < 0)
		{
			std::cerr << std::endl
					  << "[CFFMPEG_InputStream] avcodec_send_packet error code="
					  << ret << std::endl
					  << std::endl;
			return false;
		}
		while (ret >= 0)
		{
			ret = avcodec_receive_frame(ctx->pCodecCtx, ctx->pFrame);
			if (ret == AVERROR(EAGAIN)) continue;

			if (ret == AVERROR_EOF) return false;
			else if (ret < 0)
			{
				std::cerr << std::endl
						  << "[CFFMPEG_InputStream] avcodec_receive_frame "
							 "error code="
						  << ret << std::endl
						  << std::endl;
				return false;
			}

#else
		avcodec_decode_video2(
			ctx->pCodecCtx, ctx->pFrame, &frameFinished, &packet);
		if (!frameFinished)
		{
			// Free the packet that was allocated by av_read_frame
			av_packet_unref(&packet);
			continue;
		}
#endif
			// Convert the image from its native format to RGB:
			ctx->img_convert_ctx = sws_getCachedContext(
				ctx->img_convert_ctx, width, height, ctx->pCodecCtx->pix_fmt,
				width, height,
				m_grab_as_grayscale ?  // BGR vs. RGB for OpenCV
					AV_PIX_FMT_GRAY8
									: AV_PIX_FMT_BGR24,
				SWS_BICUBIC, nullptr, nullptr, nullptr);

			sws_scale(
				ctx->img_convert_ctx, ctx->pFrame->data, ctx->pFrame->linesize,
				0, height, ctx->pFrameRGB->data, ctx->pFrameRGB->linesize);

			// std::cout << "[retrieveFrame] Generating image: " <<
			// ctx->pCodecPars->width << "x" << ctx->pCodecPars->height
			// << std::endl; std::cout << "  linsize: " <<
			// ctx->pFrameRGB->linesize[0] << std::endl;

			if (ctx->pFrameRGB->linesize[0] !=
				((m_grab_as_grayscale ? 1 : 3) * width))
				THROW_EXCEPTION("FIXME: linesize!=width case not handled yet.");

			out_img.loadFromMemoryBuffer(
				width, height, !m_grab_as_grayscale, ctx->pFrameRGB->data[0]);

			// Free the packet that was allocated by av_read_frame
			av_packet_unref(&packet);
			return true;
		}
	}

	return false;  // Error reading/ EOF
#else
	return false;
#endif
}

/* --------------------------------------------------------
					getVideoFPS
   -------------------------------------------------------- */
double CFFMPEG_InputStream::getVideoFPS() const
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return -1;

	const TFFMPEGContext* ctx = &m_impl->m_state;
	if (!ctx) return -1;
	if (!ctx->pCodecCtx) return -1;

	return double(ctx->pCodecCtx->framerate.num) /
		ctx->pCodecCtx->framerate.den;
#else
	return false;
#endif
}
<commit_msg>Fix #1165<commit_after>/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2021, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#if defined(__GNUC__)  // Needed for ffmpeg headers. Only allowed here when not
// using precomp. headers
#define __STDC_CONSTANT_MACROS	// Needed for having "UINT64_C" and so
#endif
//
#include "hwdrivers-precomp.h"	// Precompiled headers
//
#include <mrpt/config.h>

#if MRPT_HAS_FFMPEG
extern "C"
{
#define _MSC_STDINT_H_	// We already have pstdint.h in MRPT
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
#endif

#include <mrpt/hwdrivers/CFFMPEG_InputStream.h>

using namespace mrpt;
using namespace mrpt::hwdrivers;

// JLBC: This file takes portions of code from the example
// "avcodec_sample.0.4.9.cpp"
//
// Minimum ffmpeg libs versions we want to support:
// Ubuntu 16.04 LTS: avcodec 56.60.100, avutil 54.31.100, avformat 56.40.101
// Ubuntu 20.04 LTS: avcodec 58.54.100, avutil 56.31.100, avformat 58.29.100
//
#if MRPT_HAS_FFMPEG
namespace mrpt::hwdrivers
{
// All context for ffmpeg:
struct TFFMPEGContext
{
	AVFormatContext* pFormatCtx{nullptr};
	int videoStream{0};
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	AVCodecParameters* pCodecPars{nullptr};
#endif
	AVCodec* pCodec{nullptr};
	AVCodecContext* pCodecCtx{nullptr};
	AVFrame* pFrame{nullptr};
	AVFrame* pFrameRGB{nullptr};
	SwsContext* img_convert_ctx{nullptr};
	std::vector<uint8_t> buffer;
};
}  // namespace mrpt::hwdrivers
#endif

struct CFFMPEG_InputStream::Impl
{
#if MRPT_HAS_FFMPEG
	TFFMPEGContext m_state;
#endif
};

/* --------------------------------------------------------
					Ctor
   -------------------------------------------------------- */
CFFMPEG_InputStream::CFFMPEG_InputStream()
#if MRPT_HAS_FFMPEG
	: m_impl(mrpt::make_impl<CFFMPEG_InputStream::Impl>())
{
// av_register_all() not needed in ffmpeg >=4.0
#if LIBAVFORMAT_VERSION_MAJOR < 58
	// Register all formats and codecs
	av_register_all();
#endif
}
#else
{
	THROW_EXCEPTION("MRPT has been compiled without FFMPEG libraries.");
}
#endif

/* --------------------------------------------------------
					Dtor
   -------------------------------------------------------- */
CFFMPEG_InputStream::~CFFMPEG_InputStream()
{
#if MRPT_HAS_FFMPEG
	// Close everything:
	this->close();
#endif
}

/* --------------------------------------------------------
					isOpen
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::isOpen() const
{
#if MRPT_HAS_FFMPEG
	const TFFMPEGContext* ctx = &m_impl->m_state;
	return ctx->pFormatCtx != nullptr;
#else
	return false;
#endif
}

/* --------------------------------------------------------
					openURL
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::openURL(
	const std::string& url, bool grab_as_grayscale, bool verbose,
	const std::map<std::string, std::string>& optionsMap)
{
#if MRPT_HAS_FFMPEG
	this->close();	// Close first

	TFFMPEGContext* ctx = &m_impl->m_state;

	this->m_url = url;
	this->m_grab_as_grayscale = grab_as_grayscale;

	AVDictionary* options = nullptr;  // "create" an empty dictionary
	for (const auto& kv : optionsMap)
		av_dict_set(&options, kv.first.c_str(), kv.second.c_str(), 0);

	// Open video file
	if (avformat_open_input(&ctx->pFormatCtx, url.c_str(), nullptr, &options) !=
		0)
	{
		ctx->pFormatCtx = nullptr;
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot open video: " << url
				  << std::endl;
		return false;
	}

	// Retrieve stream information
	if (avformat_find_stream_info(ctx->pFormatCtx, nullptr) < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Couldn't find stream "
					 "information: "
				  << url << std::endl;
		return false;
	}

	// Dump information about file onto standard error
	if (verbose) { av_dump_format(ctx->pFormatCtx, 0, url.c_str(), false); }

	// Find the first video stream
	ctx->videoStream = -1;
	for (unsigned int i = 0; i < ctx->pFormatCtx->nb_streams; i++)
	{
#if LIBAVFORMAT_VERSION_MAJOR >= 57
		auto codecType = ctx->pFormatCtx->streams[i]->codecpar->codec_type;
#else
		auto codecType = ctx->pFormatCtx->streams[i]->codec->codec_type;
#endif
		if (codecType == AVMEDIA_TYPE_VIDEO)
		{
			ctx->videoStream = (int)i;
			break;
		}
	}
	if (ctx->videoStream == -1)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] Didn't find a video stream: "
			<< url << std::endl;
		return false;
	}

	// Get a pointer to the codec context for the video stream
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	ctx->pCodecPars = ctx->pFormatCtx->streams[ctx->videoStream]->codecpar;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecPars->codec_id);
#else
	ctx->pCodecCtx = ctx->pFormatCtx->streams[ctx->videoStream]->codec;
	// Find the decoder for the video stream
	ctx->pCodec = avcodec_find_decoder(ctx->pCodecCtx->codec_id);
#endif
	if (ctx->pCodec == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Codec not found: " << url
				  << std::endl;
		return false;
	}

#if LIBAVFORMAT_VERSION_MAJOR >= 57
	ctx->pCodecCtx = avcodec_alloc_context3(nullptr /*ctx->pCodec*/);
	if (!ctx->pCodecCtx)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Cannot alloc avcodec "
					 "context for: "
				  << url << std::endl;
		return false;
	}

	// Add stream parameters to context
	if (avcodec_parameters_to_context(
			ctx->pCodecCtx,
			ctx->pFormatCtx->streams[ctx->videoStream]->codecpar))
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Failed "
					 "avcodec_parameters_to_context() for: "
				  << url << std::endl;
		return false;
	}

	// Make sure that Codecs are identical or  avcodec_open2 fails.
	ctx->pCodecCtx->codec_id = ctx->pCodec->id;
#endif

	// Open codec
	if (avcodec_open2(ctx->pCodecCtx, ctx->pCodec, nullptr) < 0)
	{
		std::cerr
			<< "[CFFMPEG_InputStream::openURL] avcodec_open2() failed for: "
			<< url << std::endl;
		return false;
	}

	// Allocate video frame
	ctx->pFrame = av_frame_alloc();
	// Allocate an AVFrame structure
	ctx->pFrameRGB = av_frame_alloc();

	if (ctx->pFrameRGB == nullptr || ctx->pFrame == nullptr)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] Could not alloc memory "
					 "for frame buffers: "
				  << url << std::endl;
		return false;
	}

	// Determine required buffer size and allocate buffer
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif
	int numBytes = av_image_get_buffer_size(
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);
	if (numBytes < 0)
	{
		std::cerr << "[CFFMPEG_InputStream::openURL] av_image_get_buffer_size "
					 "error code: "
				  << numBytes << std::endl;
		return false;
	}

	ctx->buffer.resize(numBytes);

	// Assign appropriate parts of buffer to image planes in pFrameRGB

	av_image_fill_arrays(
		ctx->pFrameRGB->data, ctx->pFrameRGB->linesize, &ctx->buffer[0],
		m_grab_as_grayscale ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_BGR24, width,
		height, 1);

	return true;  // OK.
#else
	return false;
#endif
}

/* --------------------------------------------------------
					close
   -------------------------------------------------------- */
void CFFMPEG_InputStream::close()
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return;

	TFFMPEGContext* ctx = &m_impl->m_state;

	// Close the codec
	if (ctx->pCodecCtx)
	{
		avcodec_close(ctx->pCodecCtx);
		ctx->pCodecCtx = nullptr;
	}

	// Close the video file
	if (ctx->pFormatCtx)
	{
		avformat_close_input(&ctx->pFormatCtx);
		ctx->pFormatCtx = nullptr;
	}

	// Free frames memory:
	ctx->buffer.clear();

	if (ctx->pFrameRGB)
	{
		av_frame_free(&ctx->pFrameRGB);
		ctx->pFrameRGB = nullptr;
	}
	if (ctx->pFrame)
	{
		av_frame_free(&ctx->pFrame);
		ctx->pFrame = nullptr;
	}

	if (ctx->img_convert_ctx)
	{
		sws_freeContext(ctx->img_convert_ctx);
		ctx->img_convert_ctx = nullptr;
	}

#endif
}

/* --------------------------------------------------------
					retrieveFrame
   -------------------------------------------------------- */
bool CFFMPEG_InputStream::retrieveFrame(mrpt::img::CImage& out_img)
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return false;

	TFFMPEGContext* ctx = &m_impl->m_state;

	AVPacket packet;

#if LIBAVFORMAT_VERSION_MAJOR < 57
	int frameFinished;
#endif

#if LIBAVFORMAT_VERSION_MAJOR >= 57
	const auto width = ctx->pCodecPars->width, height = ctx->pCodecPars->height;
#else
	const auto width = ctx->pCodecCtx->width, height = ctx->pCodecCtx->height;
#endif

	while (av_read_frame(ctx->pFormatCtx, &packet) >= 0)
	{
		// Is this a packet from the video stream?
		if (packet.stream_index != ctx->videoStream)
		{
			av_packet_unref(&packet);
			continue;
		}

		// Decode video frame
#if LIBAVFORMAT_VERSION_MAJOR >= 57
		int ret = avcodec_send_packet(ctx->pCodecCtx, &packet);
		if (ret < 0)
		{
			std::cerr << std::endl
					  << "[CFFMPEG_InputStream] avcodec_send_packet error code="
					  << ret << std::endl
					  << std::endl;
			return false;
		}
		while (ret >= 0)
		{
			ret = avcodec_receive_frame(ctx->pCodecCtx, ctx->pFrame);
			if (ret == AVERROR(EAGAIN)) continue;

			if (ret == AVERROR_EOF) return false;
			else if (ret < 0)
			{
				std::cerr << std::endl
						  << "[CFFMPEG_InputStream] avcodec_receive_frame "
							 "error code="
						  << ret << std::endl
						  << std::endl;
				return false;
			}

#else
		avcodec_decode_video2(
			ctx->pCodecCtx, ctx->pFrame, &frameFinished, &packet);
		if (!frameFinished)
		{
			// Free the packet that was allocated by av_read_frame
			av_packet_unref(&packet);
			continue;
		}
#endif
			// Convert the image from its native format to RGB:
			ctx->img_convert_ctx = sws_getCachedContext(
				ctx->img_convert_ctx, width, height, ctx->pCodecCtx->pix_fmt,
				width, height,
				m_grab_as_grayscale ?  // BGR vs. RGB for OpenCV
					AV_PIX_FMT_GRAY8
									: AV_PIX_FMT_BGR24,
				SWS_BICUBIC, nullptr, nullptr, nullptr);

			sws_scale(
				ctx->img_convert_ctx, ctx->pFrame->data, ctx->pFrame->linesize,
				0, height, ctx->pFrameRGB->data, ctx->pFrameRGB->linesize);

			// std::cout << "[retrieveFrame] Generating image: " <<
			// ctx->pCodecPars->width << "x" << ctx->pCodecPars->height
			// << std::endl; std::cout << "  linsize: " <<
			// ctx->pFrameRGB->linesize[0] << std::endl;

			if (ctx->pFrameRGB->linesize[0] !=
				((m_grab_as_grayscale ? 1 : 3) * width))
				THROW_EXCEPTION("FIXME: linesize!=width case not handled yet.");

			out_img.loadFromMemoryBuffer(
				width, height, !m_grab_as_grayscale, ctx->pFrameRGB->data[0]);

			// Free the packet that was allocated by av_read_frame
			av_packet_unref(&packet);
			return true;
		}
#if LIBAVFORMAT_VERSION_MAJOR >= 57
	}
#endif
	return false;  // Error reading/ EOF
#else
	return false;
#endif
}

/* --------------------------------------------------------
					getVideoFPS
   -------------------------------------------------------- */
double CFFMPEG_InputStream::getVideoFPS() const
{
#if MRPT_HAS_FFMPEG
	if (!this->isOpen()) return -1;

	const TFFMPEGContext* ctx = &m_impl->m_state;
	if (!ctx) return -1;
	if (!ctx->pCodecCtx) return -1;

	return double(ctx->pCodecCtx->framerate.num) /
		ctx->pCodecCtx->framerate.den;
#else
	return false;
#endif
}
<|endoftext|>


<commit_before>#ifndef FROG_RENDERERINGCOMPONENT_HPP
#define FROG_RENDERERINGCOMPONENT_HPP

#include "FROG/Component.hpp"
#include "FROG/ComponentHolder.hpp"
#include <SFML/Graphics.hpp>

#include <memory>

namespace frog{

    /*!
     * RenderingComponent is drawn by the scene's renderer.
     */
    class RenderingComponent : virtual public Component,
                               virtual public sf::Drawable,
                               virtual public sf::Transformable
    {

      //// attributes ////
    protected:
      std::shared_ptr<sf::Drawable>  m_drawable;

      //// operations ////
    public:

      /*!
       * @brief Constructor.
       * @param d sf::Drawable that will be kept in order to be rendered by a
       * Renderer.
       */
      RenderingComponent(sf::Drawable * const d);
      RenderingComponent(std::shared_ptr<sf::Drawable> const d);

      virtual ~RenderingComponent();

      /*!
       * @brief Draws the component in the given sf::RenderTarget.
       * @param rt RenderTarget where component should be drawn.
       * @param rs RenderStates to apply. Default : nothing.
       */
      void draw(sf::RenderTarget& rt,
                sf::RenderStates rs = sf::RenderStates::Default) const;

     /*!
       * @brief Places the component to render at the good place.
       * @details Places the component at the position of its parent,
       * so the parent need a Transform component. It also takes its rotation
       * and scale.
       * @param parent ComponentHolder holding position, rotation and scale
       */
      void update(const ComponentHolder&);

    };

}

#endif
<commit_msg>errorfix : was passing a const, causing segfault (why did that compile ?)<commit_after>#ifndef FROG_RENDERERINGCOMPONENT_HPP
#define FROG_RENDERERINGCOMPONENT_HPP

#include "FROG/Component.hpp"
#include "FROG/ComponentHolder.hpp"
#include <SFML/Graphics.hpp>

#include <memory>

namespace frog{

    /*!
     * RenderingComponent is drawn by the scene's renderer.
     */
    class RenderingComponent : virtual public Component,
                               virtual public sf::Drawable,
                               virtual public sf::Transformable
    {

      //// attributes ////
    protected:
      std::shared_ptr<sf::Drawable>  m_drawable;

      //// operations ////
    public:

      /*!
       * @brief Constructor.
       * @param d sf::Drawable that will be kept in order to be rendered by a
       * Renderer.
       */
      RenderingComponent(sf::Drawable * const d);
      RenderingComponent(std::shared_ptr<sf::Drawable> d);

      virtual ~RenderingComponent();

      /*!
       * @brief Draws the component in the given sf::RenderTarget.
       * @param rt RenderTarget where component should be drawn.
       * @param rs RenderStates to apply. Default : nothing.
       */
      void draw(sf::RenderTarget& rt,
                sf::RenderStates rs = sf::RenderStates::Default) const;

     /*!
       * @brief Places the component to render at the good place.
       * @details Places the component at the position of its parent,
       * so the parent need a Transform component. It also takes its rotation
       * and scale.
       * @param parent ComponentHolder holding position, rotation and scale
       */
      void update(const ComponentHolder&);

    };

}

#endif
<|endoftext|>


<commit_before>#include "Addressing.hpp"
#include "common.h"

void compute_levels(GlobalAddress<int64_t> level, int64_t nv, GlobalAddress<int64_t> bfs_tree, int64_t root);

int64_t verify_bfs_tree(GlobalAddress<int64_t> bfs_tree, int64_t max_bfsvtx, int64_t root, tuple_graph * tg);

template< typename T >
inline void read_array(GlobalAddress<T> base_addr, int64_t nelem, FILE* fin) {
  int64_t bufsize = 128;
  T buf[bufsize];
  for (int64_t i=0; i<nelem; i+=bufsize) {
    if (i % 51200 == 0) VLOG(3) << "reading " << i;
    int64_t n = min(nelem-i, bufsize);
    typename Incoherent<T>::RW c(base_addr+i, n, buf);
    c.block_until_acquired();
    size_t nread = fread(buf, sizeof(T), n, fin);
    CHECK(nread == n) << nread << " : " << n;
    c.block_until_released();
  }
}

template< typename T >
inline void read_array(GlobalAddress<T> base_addr, int64_t nelem, T* array) {
  int64_t bufsize = 128;
  T buf[bufsize];
  for (int64_t i=0; i<nelem; i+=bufsize) {
    if (i % 51200 == 0) VLOG(3) << "sending " << i;
    int64_t n = min(nelem-i, bufsize);
    typename Incoherent<T>::RW c(base_addr+i, n, buf);
    c.block_until_acquired();
    for (int64_t j=0; j<n; j++) c[j] = array[i+j];
    c.block_until_released();
  }
}

template< typename T >
inline void write_array(GlobalAddress<T> base_addr, int64_t nelem, FILE* fout) {
  int64_t bufsize = 128;
  T buf[bufsize];
  for (int64_t i=0; i<nelem; i+=bufsize) {
    int64_t n = min(nelem-i, bufsize);
    typename Incoherent<T>::RO c(base_addr+i, n, buf);
    c.block_until_acquired();
    fwrite(buf, sizeof(T), n, fout);
  }
}
<commit_msg>Using larger (but still safe) cache size for file IO<commit_after>#include "Addressing.hpp"
#include "common.h"

void compute_levels(GlobalAddress<int64_t> level, int64_t nv, GlobalAddress<int64_t> bfs_tree, int64_t root);

int64_t verify_bfs_tree(GlobalAddress<int64_t> bfs_tree, int64_t max_bfsvtx, int64_t root, tuple_graph * tg);

#define MAX_ACQUIRE_SIZE 3000

template< typename T >
inline void read_array(GlobalAddress<T> base_addr, int64_t nelem, FILE* fin) {
  int64_t bufsize = MAX_ACQUIRE_SIZE / sizeof(T);
  T buf[bufsize];
  for (int64_t i=0; i<nelem; i+=bufsize) {
    if (i % 51200 == 0) VLOG(3) << "reading " << i;
    int64_t n = min(nelem-i, bufsize);
    typename Incoherent<T>::RW c(base_addr+i, n, buf);
    c.block_until_acquired();
    size_t nread = fread(buf, sizeof(T), n, fin);
    CHECK(nread == n) << nread << " : " << n;
    c.block_until_released();
  }
}

template< typename T >
inline void read_array(GlobalAddress<T> base_addr, int64_t nelem, T* array) {
  int64_t bufsize = MAX_ACQUIRE_SIZE / sizeof(T);
  T buf[bufsize];
  for (int64_t i=0; i<nelem; i+=bufsize) {
    if (i % 51200 == 0) VLOG(3) << "sending " << i;
    int64_t n = min(nelem-i, bufsize);
    typename Incoherent<T>::RW c(base_addr+i, n, buf);
    c.block_until_acquired();
    for (int64_t j=0; j<n; j++) c[j] = array[i+j];
    c.block_until_released();
  }
}

template< typename T >
inline void write_array(GlobalAddress<T> base_addr, int64_t nelem, FILE* fout) {
  int64_t bufsize = MAX_ACQUIRE_SIZE / sizeof(T);
  T buf[bufsize];
  for (int64_t i=0; i<nelem; i+=bufsize) {
    int64_t n = min(nelem-i, bufsize);
    typename Incoherent<T>::RO c(base_addr+i, n, buf);
    c.block_until_acquired();
    fwrite(buf, sizeof(T), n, fout);
  }
}
<|endoftext|>