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

#include "include/android/SkAnimatedImage.h"
#include "include/codec/SkAndroidCodec.h"
#include "include/codec/SkCodec.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkPixelRef.h"
#include "src/codec/SkCodecPriv.h"
#include "src/core/SkImagePriv.h"

#include <limits.h>
#include <utility>

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
        SkISize scaledSize, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
    if (!codec) {
        return nullptr;
    }
    auto info = codec->getInfo().makeWH(scaledSize.width(), scaledSize.height());
    return Make(std::move(codec), info, cropRect, std::move(postProcess));
}

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
        const SkImageInfo& requestedInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
    if (!codec) {
        return nullptr;
    }

    auto scaledSize = requestedInfo.dimensions();
    auto decodeInfo = requestedInfo;
    if (codec->getEncodedFormat() != SkEncodedImageFormat::kWEBP
            || scaledSize.width()  >= decodeInfo.width()
            || scaledSize.height() >= decodeInfo.height()) {
        // Only libwebp can decode to arbitrary smaller sizes.
        auto dims = codec->getInfo().dimensions();
        decodeInfo = decodeInfo.makeWH(dims.width(), dims.height());
    }

    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, std::move(postProcess)));
    if (!image->fDisplayFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    return image;
}

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
    if (!codec) {
        return nullptr;
    }

    const auto decodeInfo = codec->getInfo();
    const auto scaledSize = decodeInfo.dimensions();
    const auto cropRect   = SkIRect::MakeSize(scaledSize);
    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, nullptr));

    if (!image->fDisplayFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    SkASSERT(image->fSimple);
    return image;
}

SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec, SkISize scaledSize,
        SkImageInfo decodeInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess)
    : fCodec(std::move(codec))
    , fScaledSize(scaledSize)
    , fDecodeInfo(decodeInfo)
    , fCropRect(cropRect)
    , fPostProcess(std::move(postProcess))
    , fFrameCount(fCodec->codec()->getFrameCount())
    , fSimple(fScaledSize == fDecodeInfo.dimensions() && !fPostProcess
              && fCropRect == fDecodeInfo.bounds())
    , fFinished(false)
    , fRepetitionCount(fCodec->codec()->getRepetitionCount())
    , fRepetitionsCompleted(0)
{
    if (!fDecodingFrame.fBitmap.tryAllocPixels(fDecodeInfo)) {
        return;
    }

    if (!fSimple) {
        fMatrix = SkMatrix::MakeTrans(-fCropRect.fLeft, -fCropRect.fTop);
        float scaleX = (float) fScaledSize.width()  / fDecodeInfo.width();
        float scaleY = (float) fScaledSize.height() / fDecodeInfo.height();
        fMatrix.preConcat(SkMatrix::MakeScale(scaleX, scaleY));
    }
    this->decodeNextFrame();
}

SkAnimatedImage::~SkAnimatedImage() { }

SkRect SkAnimatedImage::onGetBounds() {
    return SkRect::MakeIWH(fCropRect.width(), fCropRect.height());
}

SkAnimatedImage::Frame::Frame()
    : fIndex(SkCodec::kNoFrame)
{}

bool SkAnimatedImage::Frame::init(const SkImageInfo& info, OnInit onInit) {
    if (fBitmap.getPixels()) {
        if (fBitmap.pixelRef()->unique()) {
            SkAssertResult(fBitmap.setAlphaType(info.alphaType()));
            return true;
        }

        // An SkCanvas provided to onDraw is still holding a reference.
        // Copy before we decode to ensure that we don't overwrite the
        // expected contents of the image.
        if (OnInit::kRestoreIfNecessary == onInit) {
            SkBitmap tmp;
            if (!tmp.tryAllocPixels(info)) {
                return false;
            }

            memcpy(tmp.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
            using std::swap;
            swap(tmp, fBitmap);
            return true;
        }
    }

    return fBitmap.tryAllocPixels(info);
}

bool SkAnimatedImage::Frame::copyTo(Frame* dst) const {
    if (!dst->init(fBitmap.info(), OnInit::kNoRestore)) {
        return false;
    }

    memcpy(dst->fBitmap.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
    dst->fIndex = fIndex;
    dst->fDisposalMethod = fDisposalMethod;
    return true;
}

void SkAnimatedImage::reset() {
    fFinished = false;
    fRepetitionsCompleted = 0;
    if (fDisplayFrame.fIndex != 0) {
        fDisplayFrame.fIndex = SkCodec::kNoFrame;
        this->decodeNextFrame();
    }
}

static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose) {
    return SkCodecAnimation::DisposalMethod::kRestorePrevious == dispose;
}

int SkAnimatedImage::computeNextFrame(int current, bool* animationEnded) {
    SkASSERT(animationEnded != nullptr);
    *animationEnded = false;

    const int frameToDecode = current + 1;
    if (frameToDecode == fFrameCount - 1) {
        // Final frame. Check to determine whether to stop.
        fRepetitionsCompleted++;
        if (fRepetitionCount != SkCodec::kRepetitionCountInfinite
                && fRepetitionsCompleted > fRepetitionCount) {
            *animationEnded = true;
        }
    } else if (frameToDecode == fFrameCount) {
        return 0;
    }
    return frameToDecode;
}

double SkAnimatedImage::finish() {
    fFinished = true;
    fCurrentFrameDuration = kFinished;
    return kFinished;
}

int SkAnimatedImage::decodeNextFrame() {
    if (fFinished) {
        return kFinished;
    }

    bool animationEnded = false;
    int frameToDecode = this->computeNextFrame(fDisplayFrame.fIndex, &animationEnded);

    SkCodec::FrameInfo frameInfo;
    if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
        if (!frameInfo.fFullyReceived) {
            SkCodecPrintf("Frame %i not fully received\n", frameToDecode);
            return this->finish();
        }

        fCurrentFrameDuration = frameInfo.fDuration;
    } else {
        animationEnded = true;
        if (0 == frameToDecode) {
            // Static image. This is okay.
            frameInfo.fRequiredFrame = SkCodec::kNoFrame;
            frameInfo.fAlphaType = fCodec->getInfo().alphaType();
            frameInfo.fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep;
            // These fields won't be read.
            frameInfo.fDuration = INT_MAX;
            frameInfo.fFullyReceived = true;
            fCurrentFrameDuration = kFinished;
        } else {
            SkCodecPrintf("Error getting frameInfo for frame %i\n",
                          frameToDecode);
            return this->finish();
        }
    }

    if (frameToDecode == fDisplayFrame.fIndex) {
        if (animationEnded) {
            return this->finish();
        }
        return fCurrentFrameDuration;
    }

    for (Frame* frame : { &fRestoreFrame, &fDecodingFrame }) {
        if (frameToDecode == frame->fIndex) {
            using std::swap;
            swap(fDisplayFrame, *frame);
            if (animationEnded) {
                return this->finish();
            }
            return fCurrentFrameDuration;
        }
    }

    // The following code makes an effort to avoid overwriting a frame that will
    // be used again. If frame |i| is_restore_previous, frame |i+1| will not
    // depend on frame |i|, so do not overwrite frame |i-1|, which may be needed
    // for frame |i+1|.
    // We could be even smarter about which frames to save by looking at the
    // entire dependency chain.
    SkCodec::Options options;
    options.fFrameIndex = frameToDecode;
    if (frameInfo.fRequiredFrame == SkCodec::kNoFrame) {
        if (is_restore_previous(frameInfo.fDisposalMethod)) {
            // frameToDecode will be discarded immediately after drawing, so
            // do not overwrite a frame which could possibly be used in the
            // future.
            if (fDecodingFrame.fIndex != SkCodec::kNoFrame &&
                    !is_restore_previous(fDecodingFrame.fDisposalMethod)) {
                using std::swap;
                swap(fDecodingFrame, fRestoreFrame);
            }
        }
    } else {
        auto validPriorFrame = [&frameInfo, &frameToDecode](const Frame& frame) {
            if (SkCodec::kNoFrame == frame.fIndex ||
                    is_restore_previous(frame.fDisposalMethod)) {
                return false;
            }

            return frame.fIndex >= frameInfo.fRequiredFrame && frame.fIndex < frameToDecode;
        };
        if (validPriorFrame(fDecodingFrame)) {
            if (is_restore_previous(frameInfo.fDisposalMethod)) {
                // fDecodingFrame is a good frame to use for this one, but we
                // don't want to overwrite it.
                fDecodingFrame.copyTo(&fRestoreFrame);
            }
            options.fPriorFrame = fDecodingFrame.fIndex;
        } else if (validPriorFrame(fDisplayFrame)) {
            if (!fDisplayFrame.copyTo(&fDecodingFrame)) {
                SkCodecPrintf("Failed to allocate pixels for frame\n");
                return this->finish();
            }
            options.fPriorFrame = fDecodingFrame.fIndex;
        } else if (validPriorFrame(fRestoreFrame)) {
            if (!is_restore_previous(frameInfo.fDisposalMethod)) {
                using std::swap;
                swap(fDecodingFrame, fRestoreFrame);
            } else if (!fRestoreFrame.copyTo(&fDecodingFrame)) {
                SkCodecPrintf("Failed to restore frame\n");
                return this->finish();
            }
            options.fPriorFrame = fDecodingFrame.fIndex;
        }
    }

    auto alphaType = kOpaque_SkAlphaType == frameInfo.fAlphaType ?
                     kOpaque_SkAlphaType : kPremul_SkAlphaType;
    auto info = fDecodeInfo.makeAlphaType(alphaType);
    SkBitmap* dst = &fDecodingFrame.fBitmap;
    if (!fDecodingFrame.init(info, Frame::OnInit::kRestoreIfNecessary)) {
        return this->finish();
    }

    auto result = fCodec->codec()->getPixels(dst->info(), dst->getPixels(), dst->rowBytes(),
                                             &options);
    if (result != SkCodec::kSuccess) {
        SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, fFrameCount);
        return this->finish();
    }

    fDecodingFrame.fIndex = frameToDecode;
    fDecodingFrame.fDisposalMethod = frameInfo.fDisposalMethod;

    using std::swap;
    swap(fDecodingFrame, fDisplayFrame);
    fDisplayFrame.fBitmap.notifyPixelsChanged();

    if (animationEnded) {
        return this->finish();
    }
    return fCurrentFrameDuration;
}

void SkAnimatedImage::onDraw(SkCanvas* canvas) {
    auto image = SkMakeImageFromRasterBitmap(fDisplayFrame.fBitmap,
                                             kNever_SkCopyPixelsMode);

    if (fSimple) {
        canvas->drawImage(image, 0, 0);
        return;
    }

    SkRect bounds = this->getBounds();
    if (fPostProcess) {
        canvas->saveLayer(&bounds, nullptr);
    }
    {
        SkAutoCanvasRestore acr(canvas, fPostProcess != nullptr);
        canvas->concat(fMatrix);
        SkPaint paint;
        paint.setFilterQuality(kLow_SkFilterQuality);
        canvas->drawImage(image, 0, 0, &paint);
    }
    if (fPostProcess) {
        canvas->drawPicture(fPostProcess);
        canvas->restore();
    }
}

void SkAnimatedImage::setRepetitionCount(int newCount) {
    fRepetitionCount = newCount;
}
<commit_msg>SkAnimatedImage: Update HEIF duration<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "include/android/SkAnimatedImage.h"
#include "include/codec/SkAndroidCodec.h"
#include "include/codec/SkCodec.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkPixelRef.h"
#include "src/codec/SkCodecPriv.h"
#include "src/core/SkImagePriv.h"

#include <limits.h>
#include <utility>

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
        SkISize scaledSize, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
    if (!codec) {
        return nullptr;
    }
    auto info = codec->getInfo().makeWH(scaledSize.width(), scaledSize.height());
    return Make(std::move(codec), info, cropRect, std::move(postProcess));
}

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
        const SkImageInfo& requestedInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
    if (!codec) {
        return nullptr;
    }

    auto scaledSize = requestedInfo.dimensions();
    auto decodeInfo = requestedInfo;
    if (codec->getEncodedFormat() != SkEncodedImageFormat::kWEBP
            || scaledSize.width()  >= decodeInfo.width()
            || scaledSize.height() >= decodeInfo.height()) {
        // Only libwebp can decode to arbitrary smaller sizes.
        auto dims = codec->getInfo().dimensions();
        decodeInfo = decodeInfo.makeWH(dims.width(), dims.height());
    }

    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, std::move(postProcess)));
    if (!image->fDisplayFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    return image;
}

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
    if (!codec) {
        return nullptr;
    }

    const auto decodeInfo = codec->getInfo();
    const auto scaledSize = decodeInfo.dimensions();
    const auto cropRect   = SkIRect::MakeSize(scaledSize);
    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, nullptr));

    if (!image->fDisplayFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    SkASSERT(image->fSimple);
    return image;
}

SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec, SkISize scaledSize,
        SkImageInfo decodeInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess)
    : fCodec(std::move(codec))
    , fScaledSize(scaledSize)
    , fDecodeInfo(decodeInfo)
    , fCropRect(cropRect)
    , fPostProcess(std::move(postProcess))
    , fFrameCount(fCodec->codec()->getFrameCount())
    , fSimple(fScaledSize == fDecodeInfo.dimensions() && !fPostProcess
              && fCropRect == fDecodeInfo.bounds())
    , fFinished(false)
    , fRepetitionCount(fCodec->codec()->getRepetitionCount())
    , fRepetitionsCompleted(0)
{
    if (!fDecodingFrame.fBitmap.tryAllocPixels(fDecodeInfo)) {
        return;
    }

    if (!fSimple) {
        fMatrix = SkMatrix::MakeTrans(-fCropRect.fLeft, -fCropRect.fTop);
        float scaleX = (float) fScaledSize.width()  / fDecodeInfo.width();
        float scaleY = (float) fScaledSize.height() / fDecodeInfo.height();
        fMatrix.preConcat(SkMatrix::MakeScale(scaleX, scaleY));
    }
    this->decodeNextFrame();
}

SkAnimatedImage::~SkAnimatedImage() { }

SkRect SkAnimatedImage::onGetBounds() {
    return SkRect::MakeIWH(fCropRect.width(), fCropRect.height());
}

SkAnimatedImage::Frame::Frame()
    : fIndex(SkCodec::kNoFrame)
{}

bool SkAnimatedImage::Frame::init(const SkImageInfo& info, OnInit onInit) {
    if (fBitmap.getPixels()) {
        if (fBitmap.pixelRef()->unique()) {
            SkAssertResult(fBitmap.setAlphaType(info.alphaType()));
            return true;
        }

        // An SkCanvas provided to onDraw is still holding a reference.
        // Copy before we decode to ensure that we don't overwrite the
        // expected contents of the image.
        if (OnInit::kRestoreIfNecessary == onInit) {
            SkBitmap tmp;
            if (!tmp.tryAllocPixels(info)) {
                return false;
            }

            memcpy(tmp.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
            using std::swap;
            swap(tmp, fBitmap);
            return true;
        }
    }

    return fBitmap.tryAllocPixels(info);
}

bool SkAnimatedImage::Frame::copyTo(Frame* dst) const {
    if (!dst->init(fBitmap.info(), OnInit::kNoRestore)) {
        return false;
    }

    memcpy(dst->fBitmap.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
    dst->fIndex = fIndex;
    dst->fDisposalMethod = fDisposalMethod;
    return true;
}

void SkAnimatedImage::reset() {
    fFinished = false;
    fRepetitionsCompleted = 0;
    if (fDisplayFrame.fIndex != 0) {
        fDisplayFrame.fIndex = SkCodec::kNoFrame;
        this->decodeNextFrame();
    }
}

static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose) {
    return SkCodecAnimation::DisposalMethod::kRestorePrevious == dispose;
}

int SkAnimatedImage::computeNextFrame(int current, bool* animationEnded) {
    SkASSERT(animationEnded != nullptr);
    *animationEnded = false;

    const int frameToDecode = current + 1;
    if (frameToDecode == fFrameCount - 1) {
        // Final frame. Check to determine whether to stop.
        fRepetitionsCompleted++;
        if (fRepetitionCount != SkCodec::kRepetitionCountInfinite
                && fRepetitionsCompleted > fRepetitionCount) {
            *animationEnded = true;
        }
    } else if (frameToDecode == fFrameCount) {
        return 0;
    }
    return frameToDecode;
}

double SkAnimatedImage::finish() {
    fFinished = true;
    fCurrentFrameDuration = kFinished;
    return kFinished;
}

int SkAnimatedImage::decodeNextFrame() {
    if (fFinished) {
        return kFinished;
    }

    bool animationEnded = false;
    const int frameToDecode = this->computeNextFrame(fDisplayFrame.fIndex, &animationEnded);

    SkCodec::FrameInfo frameInfo;
    if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
        if (!frameInfo.fFullyReceived) {
            SkCodecPrintf("Frame %i not fully received\n", frameToDecode);
            return this->finish();
        }

        fCurrentFrameDuration = frameInfo.fDuration;
    } else {
        animationEnded = true;
        if (0 == frameToDecode) {
            // Static image. This is okay.
            frameInfo.fRequiredFrame = SkCodec::kNoFrame;
            frameInfo.fAlphaType = fCodec->getInfo().alphaType();
            frameInfo.fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep;
            // These fields won't be read.
            frameInfo.fDuration = INT_MAX;
            frameInfo.fFullyReceived = true;
            fCurrentFrameDuration = kFinished;
        } else {
            SkCodecPrintf("Error getting frameInfo for frame %i\n",
                          frameToDecode);
            return this->finish();
        }
    }

    if (frameToDecode == fDisplayFrame.fIndex) {
        if (animationEnded) {
            return this->finish();
        }
        return fCurrentFrameDuration;
    }

    for (Frame* frame : { &fRestoreFrame, &fDecodingFrame }) {
        if (frameToDecode == frame->fIndex) {
            using std::swap;
            swap(fDisplayFrame, *frame);
            if (animationEnded) {
                return this->finish();
            }
            return fCurrentFrameDuration;
        }
    }

    // The following code makes an effort to avoid overwriting a frame that will
    // be used again. If frame |i| is_restore_previous, frame |i+1| will not
    // depend on frame |i|, so do not overwrite frame |i-1|, which may be needed
    // for frame |i+1|.
    // We could be even smarter about which frames to save by looking at the
    // entire dependency chain.
    SkCodec::Options options;
    options.fFrameIndex = frameToDecode;
    if (frameInfo.fRequiredFrame == SkCodec::kNoFrame) {
        if (is_restore_previous(frameInfo.fDisposalMethod)) {
            // frameToDecode will be discarded immediately after drawing, so
            // do not overwrite a frame which could possibly be used in the
            // future.
            if (fDecodingFrame.fIndex != SkCodec::kNoFrame &&
                    !is_restore_previous(fDecodingFrame.fDisposalMethod)) {
                using std::swap;
                swap(fDecodingFrame, fRestoreFrame);
            }
        }
    } else {
        auto validPriorFrame = [&frameInfo, &frameToDecode](const Frame& frame) {
            if (SkCodec::kNoFrame == frame.fIndex ||
                    is_restore_previous(frame.fDisposalMethod)) {
                return false;
            }

            return frame.fIndex >= frameInfo.fRequiredFrame && frame.fIndex < frameToDecode;
        };
        if (validPriorFrame(fDecodingFrame)) {
            if (is_restore_previous(frameInfo.fDisposalMethod)) {
                // fDecodingFrame is a good frame to use for this one, but we
                // don't want to overwrite it.
                fDecodingFrame.copyTo(&fRestoreFrame);
            }
            options.fPriorFrame = fDecodingFrame.fIndex;
        } else if (validPriorFrame(fDisplayFrame)) {
            if (!fDisplayFrame.copyTo(&fDecodingFrame)) {
                SkCodecPrintf("Failed to allocate pixels for frame\n");
                return this->finish();
            }
            options.fPriorFrame = fDecodingFrame.fIndex;
        } else if (validPriorFrame(fRestoreFrame)) {
            if (!is_restore_previous(frameInfo.fDisposalMethod)) {
                using std::swap;
                swap(fDecodingFrame, fRestoreFrame);
            } else if (!fRestoreFrame.copyTo(&fDecodingFrame)) {
                SkCodecPrintf("Failed to restore frame\n");
                return this->finish();
            }
            options.fPriorFrame = fDecodingFrame.fIndex;
        }
    }

    auto alphaType = kOpaque_SkAlphaType == frameInfo.fAlphaType ?
                     kOpaque_SkAlphaType : kPremul_SkAlphaType;
    auto info = fDecodeInfo.makeAlphaType(alphaType);
    SkBitmap* dst = &fDecodingFrame.fBitmap;
    if (!fDecodingFrame.init(info, Frame::OnInit::kRestoreIfNecessary)) {
        return this->finish();
    }

    auto result = fCodec->codec()->getPixels(dst->info(), dst->getPixels(), dst->rowBytes(),
                                             &options);
    if (result != SkCodec::kSuccess) {
        SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, fFrameCount);
        return this->finish();
    }

    fDecodingFrame.fIndex = frameToDecode;
    fDecodingFrame.fDisposalMethod = frameInfo.fDisposalMethod;

    using std::swap;
    swap(fDecodingFrame, fDisplayFrame);
    fDisplayFrame.fBitmap.notifyPixelsChanged();

    if (animationEnded) {
        return this->finish();
    } else if (fCodec->getEncodedFormat() == SkEncodedImageFormat::kHEIF) {
        // HEIF doesn't know the frame duration until after decoding. Update to
        // the correct value. Note that earlier returns in this method either
        // return kFinished, or fCurrentFrameDuration. If they return the
        // latter, it is a frame that was previously decoded, so it has the
        // updated value.
        if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
            fCurrentFrameDuration = frameInfo.fDuration;
        } else {
            SkCodecPrintf("Failed to getFrameInfo on second attempt (HEIF)");
        }
    }
    return fCurrentFrameDuration;
}

void SkAnimatedImage::onDraw(SkCanvas* canvas) {
    auto image = SkMakeImageFromRasterBitmap(fDisplayFrame.fBitmap,
                                             kNever_SkCopyPixelsMode);

    if (fSimple) {
        canvas->drawImage(image, 0, 0);
        return;
    }

    SkRect bounds = this->getBounds();
    if (fPostProcess) {
        canvas->saveLayer(&bounds, nullptr);
    }
    {
        SkAutoCanvasRestore acr(canvas, fPostProcess != nullptr);
        canvas->concat(fMatrix);
        SkPaint paint;
        paint.setFilterQuality(kLow_SkFilterQuality);
        canvas->drawImage(image, 0, 0, &paint);
    }
    if (fPostProcess) {
        canvas->drawPicture(fPostProcess);
        canvas->restore();
    }
}

void SkAnimatedImage::setRepetitionCount(int newCount) {
    fRepetitionCount = newCount;
}
<|endoftext|>


<commit_before>/*
 * MIT License
 *
 * Copyright (c) 2019 Gemfield <gemfield@civilnet.cn>
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#if defined(ENABLE_RTPPROXY)
#include "mpeg-ps.h"
#include "RtpProcess.h"
#include "Util/File.h"
#include "Extension/H265.h"
#include "Extension/AAC.h"

namespace mediakit{

/**
* 合并一些时间戳相同的frame
*/
class FrameMerger {
public:
    FrameMerger() = default;
    virtual ~FrameMerger() = default;

    void inputFrame(const Frame::Ptr &frame,const function<void(uint32_t dts,uint32_t pts,const Buffer::Ptr &buffer)> &cb){
        if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) {
            Frame::Ptr back = _frameCached.back();
            Buffer::Ptr merged_frame = back;
            if(_frameCached.size() != 1){
                string merged;
                _frameCached.for_each([&](const Frame::Ptr &frame){
                    merged.append(frame->data(),frame->size());
                });
                merged_frame = std::make_shared<BufferString>(std::move(merged));
            }
            cb(back->dts(),back->pts(),merged_frame);
            _frameCached.clear();
        }
        _frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
    }
private:
    List<Frame::Ptr> _frameCached;
};

string printSSRC(uint32_t ui32Ssrc) {
    char tmp[9] = { 0 };
    ui32Ssrc = htonl(ui32Ssrc);
    uint8_t *pSsrc = (uint8_t *) &ui32Ssrc;
    for (int i = 0; i < 4; i++) {
        sprintf(tmp + 2 * i, "%02X", pSsrc[i]);
    }
    return tmp;
}

static string printAddress(const struct sockaddr *addr){
    return StrPrinter << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr) << ":" << ntohs(((struct sockaddr_in *) addr)->sin_port);
}

RtpProcess::RtpProcess(uint32_t ssrc) {
    _ssrc = ssrc;
    _track = std::make_shared<SdpTrack>();
    _track = std::make_shared<SdpTrack>();
    _track->_interleaved = 0;
    _track->_samplerate = 90000;
    _track->_type = TrackVideo;
    _track->_ssrc = _ssrc;
    DebugL << printSSRC(_ssrc);

    GET_CONFIG(bool,toRtxp,General::kPublishToRtxp);
    GET_CONFIG(bool,toHls,General::kPublishToHls);
    GET_CONFIG(bool,toMP4,General::kPublishToMP4);

    _muxer = std::make_shared<MultiMediaSourceMuxer>(DEFAULT_VHOST,"rtp",printSSRC(_ssrc),0,toRtxp,toRtxp,toHls,toMP4);

    GET_CONFIG(string,dump_dir,RtpProxy::kDumpDir);
    {
        FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".rtp",dump_dir).data(),"wb") : nullptr;
        if(fp){
            _save_file_rtp.reset(fp,[](FILE *fp){
                fclose(fp);
            });
        }
    }

    {
        FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".mp2",dump_dir).data(),"wb") : nullptr;
        if(fp){
            _save_file_ps.reset(fp,[](FILE *fp){
                fclose(fp);
            });
        }
    }

    {
        FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".video",dump_dir).data(),"wb") : nullptr;
        if(fp){
            _save_file_video.reset(fp,[](FILE *fp){
                fclose(fp);
            });
        }
    }
    _merger = std::make_shared<FrameMerger>();
}

RtpProcess::~RtpProcess() {
    if(_addr){
        DebugL << printSSRC(_ssrc) << " " << printAddress(_addr);
        delete _addr;
    }else{
        DebugL << printSSRC(_ssrc);
    }
}

bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr *addr,uint32_t *dts_out) {
    GET_CONFIG(bool,check_source,RtpProxy::kCheckSource);
    //检查源是否合法
    if(!_addr){
        _addr = new struct sockaddr;
        memcpy(_addr,addr, sizeof(struct sockaddr));
        DebugL << "RtpProcess(" << printSSRC(_ssrc) << ") bind to address:" << printAddress(_addr);
    }

    if(check_source && memcmp(_addr,addr,sizeof(struct sockaddr)) != 0){
        DebugL << "RtpProcess(" << printSSRC(_ssrc) << ") address dismatch:" << printAddress(addr) << " != " << printAddress(_addr);
        return false;
    }

    _last_rtp_time.resetTime();
    bool ret = handleOneRtp(0,_track,(unsigned char *)data,data_len);
    if(dts_out){
        *dts_out = _dts;
    }
    return ret;
}

void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) {
    if(rtp->sequence != _sequence + 1){
        WarnL << rtp->sequence << " != " << _sequence << "+1";
    }
    _sequence = rtp->sequence;

    if(_save_file_rtp){
        uint16_t  size = rtp->size() - 4;
        size = htons(size);
        fwrite((uint8_t *) &size, 2, 1, _save_file_rtp.get());
        fwrite((uint8_t *) rtp->data() + 4, rtp->size() - 4, 1, _save_file_rtp.get());
    }

    GET_CONFIG(string,rtp_type,::RtpProxy::kRtpType);
    decodeRtp(rtp->data() + 4 ,rtp->size() - 4,rtp_type.data());
}

void RtpProcess::onRtpDecode(const void *packet, int bytes, uint32_t, int flags) {
    if(_save_file_ps){
        fwrite((uint8_t *)packet,bytes, 1, _save_file_ps.get());
    }

    auto ret = decodePS((uint8_t *)packet,bytes);
    if(ret != bytes){
        WarnL << ret << " != " << bytes << " " << flags;
    }
}

void RtpProcess::onPSDecode(int stream,
                       int codecid,
                       int flags,
                       int64_t pts,
                       int64_t dts,
                       const void *data,
                       int bytes) {
    pts /= 90;
    dts /= 90;
    _stamps[codecid].revise(dts,pts,dts,pts,false);
    _dts = dts;

    switch (codecid) {
        case STREAM_VIDEO_H264: {
            if (!_codecid_video) {
                //获取到视频
                _codecid_video = codecid;
                InfoL << "got video track: H264";
                auto track = std::make_shared<H264Track>();
                _muxer->addTrack(track);
            }

            if (codecid != _codecid_video) {
                WarnL << "video track change to H264 from codecid:" << _codecid_video;
                return;
            }

            if(_save_file_video){
                fwrite((uint8_t *)data,bytes, 1, _save_file_video.get());
            }
            auto frame = std::make_shared<H264FrameNoCacheAble>((char *) data, bytes, dts, pts,0);
            _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) {
                _muxer->inputFrame(std::make_shared<H264FrameNoCacheAble>(buffer->data(), buffer->size(), dts, pts,4));
            });
            break;
        }

        case STREAM_VIDEO_H265: {
            if (!_codecid_video) {
                //获取到视频
                _codecid_video = codecid;
                InfoL << "got video track: H265";
                auto track = std::make_shared<H265Track>();
                _muxer->addTrack(track);
            }
            if (codecid != _codecid_video) {
                WarnL << "video track change to H265 from codecid:" << _codecid_video;
                return;
            }
            if(_save_file_video){
                fwrite((uint8_t *)data,bytes, 1, _save_file_video.get());
            }
            auto frame = std::make_shared<H265FrameNoCacheAble>((char *) data, bytes, dts, pts, 0);
            _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) {
                _muxer->inputFrame(std::make_shared<H265FrameNoCacheAble>(buffer->data(), buffer->size(), dts, pts, 4));
            });
            break;
        }

        case STREAM_AUDIO_AAC: {
            if (!_codecid_audio) {
                //获取到音频
                _codecid_audio = codecid;
                InfoL << "got audio track: AAC";
                auto track = std::make_shared<AACTrack>();
                _muxer->addTrack(track);
            }

            if (codecid != _codecid_audio) {
                WarnL << "audio track change to AAC from codecid:" << _codecid_audio;
                return;
            }
            _muxer->inputFrame(std::make_shared<AACFrameNoCacheAble>((char *) data, bytes, dts, 7));
            break;
        }
        default:
            WarnL << "unsupported codec type:" << codecid;
            return;
    }
}

bool RtpProcess::alive() {
    GET_CONFIG(int,timeoutSec,RtpProxy::kTimeoutSec)
    if(_last_rtp_time.elapsedTime() / 1000 < timeoutSec){
        return true;
    }
    return false;
}

string RtpProcess::get_peer_ip() {
    return inet_ntoa(((struct sockaddr_in *) _addr)->sin_addr);
}

uint16_t RtpProcess::get_peer_port() {
    return ntohs(((struct sockaddr_in *) _addr)->sin_port);
}

}//namespace mediakit
#endif//defined(ENABLE_RTPPROXY)<commit_msg>优化日志友好度<commit_after>/*
 * MIT License
 *
 * Copyright (c) 2019 Gemfield <gemfield@civilnet.cn>
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#if defined(ENABLE_RTPPROXY)
#include "mpeg-ps.h"
#include "RtpProcess.h"
#include "Util/File.h"
#include "Extension/H265.h"
#include "Extension/AAC.h"

namespace mediakit{

/**
* 合并一些时间戳相同的frame
*/
class FrameMerger {
public:
    FrameMerger() = default;
    virtual ~FrameMerger() = default;

    void inputFrame(const Frame::Ptr &frame,const function<void(uint32_t dts,uint32_t pts,const Buffer::Ptr &buffer)> &cb){
        if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) {
            Frame::Ptr back = _frameCached.back();
            Buffer::Ptr merged_frame = back;
            if(_frameCached.size() != 1){
                string merged;
                _frameCached.for_each([&](const Frame::Ptr &frame){
                    merged.append(frame->data(),frame->size());
                });
                merged_frame = std::make_shared<BufferString>(std::move(merged));
            }
            cb(back->dts(),back->pts(),merged_frame);
            _frameCached.clear();
        }
        _frameCached.emplace_back(Frame::getCacheAbleFrame(frame));
    }
private:
    List<Frame::Ptr> _frameCached;
};

string printSSRC(uint32_t ui32Ssrc) {
    char tmp[9] = { 0 };
    ui32Ssrc = htonl(ui32Ssrc);
    uint8_t *pSsrc = (uint8_t *) &ui32Ssrc;
    for (int i = 0; i < 4; i++) {
        sprintf(tmp + 2 * i, "%02X", pSsrc[i]);
    }
    return tmp;
}

static string printAddress(const struct sockaddr *addr){
    return StrPrinter << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr) << ":" << ntohs(((struct sockaddr_in *) addr)->sin_port);
}

RtpProcess::RtpProcess(uint32_t ssrc) {
    _ssrc = ssrc;
    _track = std::make_shared<SdpTrack>();
    _track = std::make_shared<SdpTrack>();
    _track->_interleaved = 0;
    _track->_samplerate = 90000;
    _track->_type = TrackVideo;
    _track->_ssrc = _ssrc;
    DebugL << printSSRC(_ssrc);

    GET_CONFIG(bool,toRtxp,General::kPublishToRtxp);
    GET_CONFIG(bool,toHls,General::kPublishToHls);
    GET_CONFIG(bool,toMP4,General::kPublishToMP4);

    _muxer = std::make_shared<MultiMediaSourceMuxer>(DEFAULT_VHOST,"rtp",printSSRC(_ssrc),0,toRtxp,toRtxp,toHls,toMP4);

    GET_CONFIG(string,dump_dir,RtpProxy::kDumpDir);
    {
        FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".rtp",dump_dir).data(),"wb") : nullptr;
        if(fp){
            _save_file_rtp.reset(fp,[](FILE *fp){
                fclose(fp);
            });
        }
    }

    {
        FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".mp2",dump_dir).data(),"wb") : nullptr;
        if(fp){
            _save_file_ps.reset(fp,[](FILE *fp){
                fclose(fp);
            });
        }
    }

    {
        FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".video",dump_dir).data(),"wb") : nullptr;
        if(fp){
            _save_file_video.reset(fp,[](FILE *fp){
                fclose(fp);
            });
        }
    }
    _merger = std::make_shared<FrameMerger>();
}

RtpProcess::~RtpProcess() {
    if(_addr){
        DebugL << printSSRC(_ssrc) << " " << printAddress(_addr);
        delete _addr;
    }else{
        DebugL << printSSRC(_ssrc);
    }
}

bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr *addr,uint32_t *dts_out) {
    GET_CONFIG(bool,check_source,RtpProxy::kCheckSource);
    //检查源是否合法
    if(!_addr){
        _addr = new struct sockaddr;
        memcpy(_addr,addr, sizeof(struct sockaddr));
        DebugL << "RtpProcess(" << printSSRC(_ssrc) << ") bind to address:" << printAddress(_addr);
    }

    if(check_source && memcmp(_addr,addr,sizeof(struct sockaddr)) != 0){
        DebugL << "RtpProcess(" << printSSRC(_ssrc) << ") address dismatch:" << printAddress(addr) << " != " << printAddress(_addr);
        return false;
    }

    _last_rtp_time.resetTime();
    bool ret = handleOneRtp(0,_track,(unsigned char *)data,data_len);
    if(dts_out){
        *dts_out = _dts;
    }
    return ret;
}

void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) {
    if(rtp->sequence != _sequence + 1){
        WarnL << rtp->sequence << " != " << _sequence << "+1";
    }
    _sequence = rtp->sequence;

    if(_save_file_rtp){
        uint16_t  size = rtp->size() - 4;
        size = htons(size);
        fwrite((uint8_t *) &size, 2, 1, _save_file_rtp.get());
        fwrite((uint8_t *) rtp->data() + 4, rtp->size() - 4, 1, _save_file_rtp.get());
    }

    GET_CONFIG(string,rtp_type,::RtpProxy::kRtpType);
    decodeRtp(rtp->data() + 4 ,rtp->size() - 4,rtp_type.data());
}

void RtpProcess::onRtpDecode(const void *packet, int bytes, uint32_t, int flags) {
    if(_save_file_ps){
        fwrite((uint8_t *)packet,bytes, 1, _save_file_ps.get());
    }

    auto ret = decodePS((uint8_t *)packet,bytes);
    if(ret != bytes){
        WarnL << ret << " != " << bytes << " " << flags;
    }
}

#define SWITCH_CASE(codec_id) case codec_id : return #codec_id
static const char *getCodecName(int codec_id) {
    switch (codec_id) {
        SWITCH_CASE(STREAM_VIDEO_MPEG4);
        SWITCH_CASE(STREAM_VIDEO_H264);
        SWITCH_CASE(STREAM_VIDEO_H265);
        SWITCH_CASE(STREAM_VIDEO_SVAC);
        SWITCH_CASE(STREAM_AUDIO_MP3);
        SWITCH_CASE(STREAM_AUDIO_AAC);
        SWITCH_CASE(STREAM_AUDIO_G711);
        SWITCH_CASE(STREAM_AUDIO_G722);
        SWITCH_CASE(STREAM_AUDIO_G723);
        SWITCH_CASE(STREAM_AUDIO_G729);
        SWITCH_CASE(STREAM_AUDIO_SVAC);
        default:
            return "unknown codec";
    }
}

void RtpProcess::onPSDecode(int stream,
                       int codecid,
                       int flags,
                       int64_t pts,
                       int64_t dts,
                       const void *data,
                       int bytes) {
    pts /= 90;
    dts /= 90;
    _stamps[codecid].revise(dts,pts,dts,pts,false);
    _dts = dts;

    switch (codecid) {
        case STREAM_VIDEO_H264: {
            if (!_codecid_video) {
                //获取到视频
                _codecid_video = codecid;
                InfoL << "got video track: H264";
                auto track = std::make_shared<H264Track>();
                _muxer->addTrack(track);
            }

            if (codecid != _codecid_video) {
                WarnL << "video track change to H264 from codecid:" << getCodecName(_codecid_video);
                return;
            }

            if(_save_file_video){
                fwrite((uint8_t *)data,bytes, 1, _save_file_video.get());
            }
            auto frame = std::make_shared<H264FrameNoCacheAble>((char *) data, bytes, dts, pts,0);
            _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) {
                _muxer->inputFrame(std::make_shared<H264FrameNoCacheAble>(buffer->data(), buffer->size(), dts, pts,4));
            });
            break;
        }

        case STREAM_VIDEO_H265: {
            if (!_codecid_video) {
                //获取到视频
                _codecid_video = codecid;
                InfoL << "got video track: H265";
                auto track = std::make_shared<H265Track>();
                _muxer->addTrack(track);
            }
            if (codecid != _codecid_video) {
                WarnL << "video track change to H265 from codecid:" << getCodecName(_codecid_video);
                return;
            }
            if(_save_file_video){
                fwrite((uint8_t *)data,bytes, 1, _save_file_video.get());
            }
            auto frame = std::make_shared<H265FrameNoCacheAble>((char *) data, bytes, dts, pts, 0);
            _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) {
                _muxer->inputFrame(std::make_shared<H265FrameNoCacheAble>(buffer->data(), buffer->size(), dts, pts, 4));
            });
            break;
        }

        case STREAM_AUDIO_AAC: {
            if (!_codecid_audio) {
                //获取到音频
                _codecid_audio = codecid;
                InfoL << "got audio track: AAC";
                auto track = std::make_shared<AACTrack>();
                _muxer->addTrack(track);
            }

            if (codecid != _codecid_audio) {
                WarnL << "audio track change to AAC from codecid:" << getCodecName(_codecid_audio);
                return;
            }
            _muxer->inputFrame(std::make_shared<AACFrameNoCacheAble>((char *) data, bytes, dts, 7));
            break;
        }
        default:
            if(codecid != 0){
                WarnL << "unsupported codec type:" << getCodecName(codecid);
            }
            return;
    }
}

bool RtpProcess::alive() {
    GET_CONFIG(int,timeoutSec,RtpProxy::kTimeoutSec)
    if(_last_rtp_time.elapsedTime() / 1000 < timeoutSec){
        return true;
    }
    return false;
}

string RtpProcess::get_peer_ip() {
    return inet_ntoa(((struct sockaddr_in *) _addr)->sin_addr);
}

uint16_t RtpProcess::get_peer_port() {
    return ntohs(((struct sockaddr_in *) _addr)->sin_port);
}

}//namespace mediakit
#endif//defined(ENABLE_RTPPROXY)<|endoftext|>


<commit_before>// Author: Sudeep Pillai (spillai@csail.mit.edu)
// Note: Stripped from Opencv (opencv/modules/python/src2/cv2.cpp)

# include "conversion.h"
/*
 * The following conversion functions are taken/adapted from OpenCV's cv2.cpp file
 * inside modules/python/src2 folder.
 */

static void init()
{
    import_array();
}

static int failmsg(const char *fmt, ...)
{
    char str[1000];

    va_list ap;
    va_start(ap, fmt);
    vsnprintf(str, sizeof(str), fmt, ap);
    va_end(ap);

    PyErr_SetString(PyExc_TypeError, str);
    return 0;
}

class PyAllowThreads
{
public:
    PyAllowThreads() : _state(PyEval_SaveThread()) {}
    ~PyAllowThreads()
    {
        PyEval_RestoreThread(_state);
    }
private:
    PyThreadState* _state;
};

class PyEnsureGIL
{
public:
    PyEnsureGIL() : _state(PyGILState_Ensure()) {}
    ~PyEnsureGIL()
    {
        PyGILState_Release(_state);
    }
private:
    PyGILState_STATE _state;
};

using namespace cv;
static PyObject* failmsgp(const char *fmt, ...)
{
  char str[1000];

  va_list ap;
  va_start(ap, fmt);
  vsnprintf(str, sizeof(str), fmt, ap);
  va_end(ap);

  PyErr_SetString(PyExc_TypeError, str);
  return 0;
}

#define OPENCV_3 0
#if OPENCV_3
class NumpyAllocator : public MatAllocator
{
public:
    NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); }
    ~NumpyAllocator() {}

    UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const
    {
        UMatData* u = new UMatData(this);
        u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o);
        npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
        for( int i = 0; i < dims - 1; i++ )
            step[i] = (size_t)_strides[i];
        step[dims-1] = CV_ELEM_SIZE(type);
        u->size = sizes[0]*step[0];
        u->userdata = o;
        return u;
    }

    UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, int flags) const
    {
        if( data != 0 )
        {
            CV_Error(Error::StsAssert, "The data should normally be NULL!");
            // probably this is safe to do in such extreme case
            return stdAllocator->allocate(dims0, sizes, type, data, step, flags);
        }
        PyEnsureGIL gil;

        int depth = CV_MAT_DEPTH(type);
        int cn = CV_MAT_CN(type);
        const int f = (int)(sizeof(size_t)/8);
        int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
        depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
        depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
        depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
        int i, dims = dims0;
        cv::AutoBuffer<npy_intp> _sizes(dims + 1);
        for( i = 0; i < dims; i++ )
            _sizes[i] = sizes[i];
        if( cn > 1 )
            _sizes[dims++] = cn;
        PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
        if(!o)
            CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
        return allocate(o, dims0, sizes, type, step);
    }

    bool allocate(UMatData* u, int accessFlags) const
    {
        return stdAllocator->allocate(u, accessFlags);
    }

    void deallocate(UMatData* u) const
    {
        if(u)
        {
            PyEnsureGIL gil;
            PyObject* o = (PyObject*)u->userdata;
            Py_XDECREF(o);
            delete u;
        }
    }

    const MatAllocator* stdAllocator;
};
#else
class NumpyAllocator : public MatAllocator
{
public:
    NumpyAllocator() {}
    ~NumpyAllocator() {}

    void allocate(int dims, const int* sizes, int type, int*& refcount,
                  uchar*& datastart, uchar*& data, size_t* step)
    {
        PyEnsureGIL gil;

        int depth = CV_MAT_DEPTH(type);
        int cn = CV_MAT_CN(type);
        const int f = (int)(sizeof(size_t)/8);
        int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
                      depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
                      depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
                      depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
        int i;
        npy_intp _sizes[CV_MAX_DIM+1];
        for( i = 0; i < dims; i++ )
            _sizes[i] = sizes[i];
        if( cn > 1 )
        {
            /*if( _sizes[dims-1] == 1 )
                _sizes[dims-1] = cn;
            else*/
                _sizes[dims++] = cn;
        }
        PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
        if(!o)
            CV_Error_(CV_StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
        refcount = refcountFromPyObject(o);
        npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
        for( i = 0; i < dims - (cn > 1); i++ )
            step[i] = (size_t)_strides[i];
        datastart = data = (uchar*)PyArray_DATA((PyArrayObject*) o);
    }

    void deallocate(int* refcount, uchar*, uchar*)
    {
        PyEnsureGIL gil;
        if( !refcount )
            return;
        PyObject* o = pyObjectFromRefcount(refcount);
        Py_INCREF(o);
        Py_DECREF(o);
    }
};
#endif

NumpyAllocator g_numpyAllocator;

NDArrayConverter::NDArrayConverter() { init(); }

void NDArrayConverter::init()
{
    import_array();
}

cv::Mat NDArrayConverter::toMat(const PyObject *o)
{
    cv::Mat m;

    if(!o || o == Py_None)
    {
        if( !m.data )
            m.allocator = &g_numpyAllocator;
    }

    if( !PyArray_Check(o) )
    {
        failmsg("toMat: Object is not a numpy array");
    }

    int typenum = PyArray_TYPE(o);
    int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S :
               typenum == NPY_USHORT ? CV_16U : typenum == NPY_SHORT ? CV_16S :
               typenum == NPY_INT || typenum == NPY_LONG ? CV_32S :
               typenum == NPY_FLOAT ? CV_32F :
               typenum == NPY_DOUBLE ? CV_64F : -1;

    if( type < 0 )
    {
        failmsg("toMat: Data type = %d is not supported", typenum);
    }

    int ndims = PyArray_NDIM(o);

    if(ndims >= CV_MAX_DIM)
    {
        failmsg("toMat: Dimensionality (=%d) is too high", ndims);
    }

    int size[CV_MAX_DIM+1];
    size_t step[CV_MAX_DIM+1], elemsize = CV_ELEM_SIZE1(type);
    const npy_intp* _sizes = PyArray_DIMS(o);
    const npy_intp* _strides = PyArray_STRIDES(o);
    bool transposed = false;

    for(int i = 0; i < ndims; i++)
    {
        size[i] = (int)_sizes[i];
        step[i] = (size_t)_strides[i];
    }

    if( ndims == 0 || step[ndims-1] > elemsize ) {
        size[ndims] = 1;
        step[ndims] = elemsize;
        ndims++;
    }

    if( ndims >= 2 && step[0] < step[1] )
    {
        std::swap(size[0], size[1]);
        std::swap(step[0], step[1]);
        transposed = true;
    }

    // std::cerr << " ndims: " << ndims
    //           << " size: " << size
    //           << " type: " << type
    //           << " step: " << step
    //           << " size: " << size[2] << std::endl;

    // TODO: Possible bug in multi-dimensional matrices
#if 0
    if( ndims == 3 && size[2] <= CV_CN_MAX && step[1] == elemsize*size[2] )
    {
        ndims--;
        type |= CV_MAKETYPE(0, size[2]);
    }
#endif

    if( ndims > 2)
    {
        failmsg("toMat: Object has more than 2 dimensions");
    }

    m = Mat(ndims, size, type, PyArray_DATA(o), step);
    // m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);

    if( m.data )
    {
#if OPENCV_3
      m.addref();
      Py_INCREF(o);
#else
        m.refcount = refcountFromPyObject(o);
        m.addref(); // protect the original numpy array from deallocation
                    // (since Mat destructor will decrement the reference counter)
#endif
    };
    m.allocator = &g_numpyAllocator;

    if( transposed )
    {
        Mat tmp;
        tmp.allocator = &g_numpyAllocator;
        transpose(m, tmp);
        m = tmp;
    }
    return m;
}

PyObject* NDArrayConverter::toNDArray(const cv::Mat& m)
{
#if OPENCV_3
  if( !m.data )
        Py_RETURN_NONE;
    Mat temp, *p = (Mat*)&m;
    if(!p->u || p->allocator != &g_numpyAllocator)
    {
        temp.allocator = &g_numpyAllocator;
        m.copyTo(temp);
        p = &temp;
    }
    PyObject* o = (PyObject*)p->u->userdata;
    Py_INCREF(o);
    // p->addref();
    // pyObjectFromRefcount(p->refcount);
    return o;
#else
    if( !m.data )
      Py_RETURN_NONE;
    Mat temp, *p = (Mat*)&m;
    if(!p->refcount || p->allocator != &g_numpyAllocator)
    {
        temp.allocator = &g_numpyAllocator;
        ERRWRAP2(m.copyTo(temp));
        p = &temp;
    }
    p->addref();
    return pyObjectFromRefcount(p->refcount);
#endif

}
<commit_msg>Allow multi-channel images by default<commit_after>// Author: Sudeep Pillai (spillai@csail.mit.edu)
// Note: Stripped from Opencv (opencv/modules/python/src2/cv2.cpp)

# include "conversion.h"
/*
 * The following conversion functions are taken/adapted from OpenCV's cv2.cpp file
 * inside modules/python/src2 folder.
 */

static void init()
{
    import_array();
}

static int failmsg(const char *fmt, ...)
{
    char str[1000];

    va_list ap;
    va_start(ap, fmt);
    vsnprintf(str, sizeof(str), fmt, ap);
    va_end(ap);

    PyErr_SetString(PyExc_TypeError, str);
    return 0;
}

class PyAllowThreads
{
public:
    PyAllowThreads() : _state(PyEval_SaveThread()) {}
    ~PyAllowThreads()
    {
        PyEval_RestoreThread(_state);
    }
private:
    PyThreadState* _state;
};

class PyEnsureGIL
{
public:
    PyEnsureGIL() : _state(PyGILState_Ensure()) {}
    ~PyEnsureGIL()
    {
        PyGILState_Release(_state);
    }
private:
    PyGILState_STATE _state;
};

using namespace cv;
static PyObject* failmsgp(const char *fmt, ...)
{
  char str[1000];

  va_list ap;
  va_start(ap, fmt);
  vsnprintf(str, sizeof(str), fmt, ap);
  va_end(ap);

  PyErr_SetString(PyExc_TypeError, str);
  return 0;
}

#define OPENCV_3 0
#if OPENCV_3
class NumpyAllocator : public MatAllocator
{
public:
    NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); }
    ~NumpyAllocator() {}

    UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const
    {
        UMatData* u = new UMatData(this);
        u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o);
        npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
        for( int i = 0; i < dims - 1; i++ )
            step[i] = (size_t)_strides[i];
        step[dims-1] = CV_ELEM_SIZE(type);
        u->size = sizes[0]*step[0];
        u->userdata = o;
        return u;
    }

    UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, int flags) const
    {
        if( data != 0 )
        {
            CV_Error(Error::StsAssert, "The data should normally be NULL!");
            // probably this is safe to do in such extreme case
            return stdAllocator->allocate(dims0, sizes, type, data, step, flags);
        }
        PyEnsureGIL gil;

        int depth = CV_MAT_DEPTH(type);
        int cn = CV_MAT_CN(type);
        const int f = (int)(sizeof(size_t)/8);
        int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
        depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
        depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
        depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
        int i, dims = dims0;
        cv::AutoBuffer<npy_intp> _sizes(dims + 1);
        for( i = 0; i < dims; i++ )
            _sizes[i] = sizes[i];
        if( cn > 1 )
            _sizes[dims++] = cn;
        PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
        if(!o)
            CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
        return allocate(o, dims0, sizes, type, step);
    }

    bool allocate(UMatData* u, int accessFlags) const
    {
        return stdAllocator->allocate(u, accessFlags);
    }

    void deallocate(UMatData* u) const
    {
        if(u)
        {
            PyEnsureGIL gil;
            PyObject* o = (PyObject*)u->userdata;
            Py_XDECREF(o);
            delete u;
        }
    }

    const MatAllocator* stdAllocator;
};
#else
class NumpyAllocator : public MatAllocator
{
public:
    NumpyAllocator() {}
    ~NumpyAllocator() {}

    void allocate(int dims, const int* sizes, int type, int*& refcount,
                  uchar*& datastart, uchar*& data, size_t* step)
    {
        PyEnsureGIL gil;

        int depth = CV_MAT_DEPTH(type);
        int cn = CV_MAT_CN(type);
        const int f = (int)(sizeof(size_t)/8);
        int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
                      depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
                      depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
                      depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
        int i;
        npy_intp _sizes[CV_MAX_DIM+1];
        for( i = 0; i < dims; i++ )
            _sizes[i] = sizes[i];
        if( cn > 1 )
        {
            /*if( _sizes[dims-1] == 1 )
                _sizes[dims-1] = cn;
            else*/
                _sizes[dims++] = cn;
        }
        PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
        if(!o)
            CV_Error_(CV_StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
        refcount = refcountFromPyObject(o);
        npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
        for( i = 0; i < dims - (cn > 1); i++ )
            step[i] = (size_t)_strides[i];
        datastart = data = (uchar*)PyArray_DATA((PyArrayObject*) o);
    }

    void deallocate(int* refcount, uchar*, uchar*)
    {
        PyEnsureGIL gil;
        if( !refcount )
            return;
        PyObject* o = pyObjectFromRefcount(refcount);
        Py_INCREF(o);
        Py_DECREF(o);
    }
};
#endif

NumpyAllocator g_numpyAllocator;

NDArrayConverter::NDArrayConverter() { init(); }

void NDArrayConverter::init()
{
    import_array();
}

cv::Mat NDArrayConverter::toMat(const PyObject *o)
{
    cv::Mat m;

    if(!o || o == Py_None)
    {
        if( !m.data )
            m.allocator = &g_numpyAllocator;
    }

    if( !PyArray_Check(o) )
    {
        failmsg("toMat: Object is not a numpy array");
    }

    int typenum = PyArray_TYPE(o);
    int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S :
               typenum == NPY_USHORT ? CV_16U : typenum == NPY_SHORT ? CV_16S :
               typenum == NPY_INT || typenum == NPY_LONG ? CV_32S :
               typenum == NPY_FLOAT ? CV_32F :
               typenum == NPY_DOUBLE ? CV_64F : -1;

    if( type < 0 )
    {
        failmsg("toMat: Data type = %d is not supported", typenum);
    }

    int ndims = PyArray_NDIM(o);

    if(ndims >= CV_MAX_DIM)
    {
        failmsg("toMat: Dimensionality (=%d) is too high", ndims);
    }

    int size[CV_MAX_DIM+1];
    size_t step[CV_MAX_DIM+1], elemsize = CV_ELEM_SIZE1(type);
    const npy_intp* _sizes = PyArray_DIMS(o);
    const npy_intp* _strides = PyArray_STRIDES(o);
    bool transposed = false;

    for(int i = 0; i < ndims; i++)
    {
        size[i] = (int)_sizes[i];
        step[i] = (size_t)_strides[i];
    }

    if( ndims == 0 || step[ndims-1] > elemsize ) {
        size[ndims] = 1;
        step[ndims] = elemsize;
        ndims++;
    }

    if( ndims >= 2 && step[0] < step[1] )
    {
        std::swap(size[0], size[1]);
        std::swap(step[0], step[1]);
        transposed = true;
    }

    // std::cerr << " ndims: " << ndims
    //           << " size: " << size
    //           << " type: " << type
    //           << " step: " << step
    //           << " size: " << size[2] << std::endl;

    // TODO: Possible bug in multi-dimensional matrices
#if 1
    if( ndims == 3 && size[2] <= CV_CN_MAX && step[1] == elemsize*size[2] )
    {
        ndims--;
        type |= CV_MAKETYPE(0, size[2]);
    }
#endif

    if( ndims > 2)
    {
        failmsg("toMat: Object has more than 2 dimensions");
    }

    m = Mat(ndims, size, type, PyArray_DATA(o), step);
    // m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);

    if( m.data )
    {
#if OPENCV_3
      m.addref();
      Py_INCREF(o);
#else
        m.refcount = refcountFromPyObject(o);
        m.addref(); // protect the original numpy array from deallocation
                    // (since Mat destructor will decrement the reference counter)
#endif
    };
    m.allocator = &g_numpyAllocator;

    if( transposed )
    {
        Mat tmp;
        tmp.allocator = &g_numpyAllocator;
        transpose(m, tmp);
        m = tmp;
    }
    return m;
}

PyObject* NDArrayConverter::toNDArray(const cv::Mat& m)
{
#if OPENCV_3
  if( !m.data )
        Py_RETURN_NONE;
    Mat temp, *p = (Mat*)&m;
    if(!p->u || p->allocator != &g_numpyAllocator)
    {
        temp.allocator = &g_numpyAllocator;
        m.copyTo(temp);
        p = &temp;
    }
    PyObject* o = (PyObject*)p->u->userdata;
    Py_INCREF(o);
    // p->addref();
    // pyObjectFromRefcount(p->refcount);
    return o;
#else
    if( !m.data )
      Py_RETURN_NONE;
    Mat temp, *p = (Mat*)&m;
    if(!p->refcount || p->allocator != &g_numpyAllocator)
    {
        temp.allocator = &g_numpyAllocator;
        ERRWRAP2(m.copyTo(temp));
        p = &temp;
    }
    p->addref();
    return pyObjectFromRefcount(p->refcount);
#endif

}
<|endoftext|>


<commit_before>
#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#include <simgear/compiler.h>

#include <iostream>
#include <cstdlib>
#include <cstdio>

#include <simgear/misc/sg_dir.hxx>

#include "sg_binobj.hxx"

using std::cout;
using std::cerr;
using std::endl;
using std::string;

#define COMPARE(a, b) \
    if ((a) != (b))  { \
        cerr << "failed:" << #a << " != " << #b << endl; \
        cerr << "\tgot:" << a << endl; \
        exit(1); \
    }

#define VERIFY(a) \
    if (!(a))  { \
        cerr << "failed:" << #a << endl; \
        exit(1); \
    }

void generate_points(int count, std::vector<SGVec3d>& vec)
{
    for (int i=0; i<count; ++i) {
        vec.push_back(SGVec3d(i * 0.5, i * i, i * 4));
    }
}

void generate_normals(int count, std::vector<SGVec3f>& vec)
{
    for (int i=0; i<count; ++i) {
        vec.push_back(normalize(SGVec3f(i, i * 2, i * -4)));
    }
}

void generate_tcs(int count, std::vector<SGVec2f>& vec)
{
    for (int i=0; i<count; ++i) {
        vec.push_back(SGVec2f(1.0 / i, 16.0 / i));
    }
}

void test_empty()
{
    SGBinObject empty;
    SGPath path(simgear::Dir::current().file("empty.btg.gz"));
    bool ok = empty.write_bin_file(path);
    VERIFY( ok );
    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);

   COMPARE(rd.get_wgs84_nodes().size(), 0);
   VERIFY(rd.get_pt_materials().empty());
}

void comparePoints(const SGBinObject& rd, const std::vector<SGVec3d>& b)
{
    for (unsigned int i=1; i<b.size(); i += 10) {
        SGVec3d pos = rd.get_wgs84_nodes()[i];
        pos += rd.get_gbs_center();

       if (!equivalent(pos, b[i], 0.1)) {
            cout << "i=" << i << endl;
            cout << b[i] << endl;
            cout << pos << endl;
        }

        VERIFY(equivalent(pos, b[i], 0.1));
    }
}

void compareTexCoords(const SGBinObject& rd, const std::vector<SGVec2f>& b)
{
    for (unsigned int i=1; i<b.size(); i += 10) {
        SGVec2f pos = rd.get_texcoords()[i];
        VERIFY(equivalent(pos, b[i], 0.001f));
    }
}

int_list make_tri(int maxIndex)
{
    int_list r;
    r.push_back(random() % maxIndex);
    r.push_back(random() % maxIndex);
    r.push_back(random() % maxIndex);
    return r;
}

void compareTris(const SGBinObject& a, const SGBinObject& b)
{
    unsigned int count = a.get_tri_materials().size();
    for (unsigned int i=0; i<count; i += 39) {
        const int_list& vA(a.get_tris_v()[i]);
        const int_list& vB(b.get_tris_v()[i]);
        VERIFY(vA == vB);

        COMPARE(a.get_tri_materials()[i], b.get_tri_materials()[i]);

        const int_list& tA(a.get_tris_tc()[i]);
        const int_list& tB(b.get_tris_tc()[i]);
        VERIFY(tA == tB);
    }
}

void generate_tris(SGBinObject& b, int count)
{
    group_list v, n, tc;
    string_list materials;

    int maxVertices = b.get_wgs84_nodes().size();
    int maxNormals = b.get_normals().size();
    int maxTCs = b.get_texcoords().size();

    for (int t=0; t<count; ++t) {
        v.push_back(make_tri(maxVertices));
        n.push_back(make_tri(maxNormals));
        tc.push_back(make_tri(maxTCs));
        materials.push_back("material1");
    }

    b.set_tris_v(v);
    b.set_tris_n(n);
    b.set_tris_tc(tc);
    b.set_tri_materials(materials);
}

void test_basic()
{
    SGBinObject basic;
    SGPath path(simgear::Dir::current().file("basic.btg.gz"));

    SGVec3d center(1, 2, 3);
    basic.set_gbs_center(center);
    basic.set_gbs_radius(12345);

    std::vector<SGVec3d> points;
    generate_points(1024, points);
    std::vector<SGVec3f> normals;
    generate_normals(1024, normals);
    std::vector<SGVec2f> texCoords;
    generate_tcs(10000, texCoords);

    basic.set_wgs84_nodes(points);
    basic.set_normals(normals);
    basic.set_texcoords(texCoords);

    bool ok = basic.write_bin_file(path);
    VERIFY( ok );

    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);
   COMPARE(rd.get_version(), 7); // should be version 7 since indices are < 2^16
   COMPARE(rd.get_gbs_center(), center);
   COMPARE(rd.get_gbs_radius(), 12345);
   COMPARE(rd.get_wgs84_nodes().size(), points.size());

   comparePoints(rd, points);
   compareTexCoords(rd, texCoords);
}

void test_many_tcs()
{
    SGBinObject basic;
    SGPath path(simgear::Dir::current().file("many_tex.btg.gz"));

    SGVec3d center(1, 2, 3);
    basic.set_gbs_center(center);
    basic.set_gbs_radius(12345);

    std::vector<SGVec3d> points;
    generate_points(10000, points);
    std::vector<SGVec3f> normals;
    generate_normals(1024, normals);
    std::vector<SGVec2f> texCoords;
    generate_tcs(100000, texCoords);

    basic.set_wgs84_nodes(points);
    basic.set_normals(normals);
    basic.set_texcoords(texCoords);

    generate_tris(basic, 20000);

    bool ok = basic.write_bin_file(path);
    VERIFY( ok );

    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);
   COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
   COMPARE(rd.get_wgs84_nodes().size(), points.size());
   COMPARE(rd.get_texcoords().size(), texCoords.size());

   comparePoints(rd, points);
   compareTexCoords(rd, texCoords);
   compareTris(basic, rd);
}

void test_big()
{
    SGBinObject basic;
    SGPath path(simgear::Dir::current().file("big.btg.gz"));

    SGVec3d center(1, 2, 3);
    basic.set_gbs_center(center);
    basic.set_gbs_radius(12345);

    std::vector<SGVec3d> points;
    generate_points(200000, points);
    std::vector<SGVec3f> normals;
    generate_normals(1024, normals);
    std::vector<SGVec2f> texCoords;
    generate_tcs(300000, texCoords);

    basic.set_wgs84_nodes(points);
    basic.set_normals(normals);
    basic.set_texcoords(texCoords);

    generate_tris(basic, 200000);

    bool ok = basic.write_bin_file(path);
    VERIFY( ok );

    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);
   COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
   COMPARE(rd.get_wgs84_nodes().size(), points.size());
   COMPARE(rd.get_texcoords().size(), texCoords.size());

   comparePoints(rd, points);
   compareTexCoords(rd, texCoords);
   compareTris(basic, rd);
}

int main(int argc, char* argv[])
{
    test_empty();
    test_basic();
    test_many_tcs();
    test_big();

    return 0;
}
<commit_msg>Fix windows build<commit_after>
#ifdef HAVE_CONFIG_H
#  include <simgear_config.h>
#endif

#include <simgear/compiler.h>

#include <iostream>
#include <cstdlib>
#include <cstdio>

#ifdef _MSC_VER
#   define  random  rand
#endif

#include <simgear/misc/sg_dir.hxx>

#include "sg_binobj.hxx"

using std::cout;
using std::cerr;
using std::endl;
using std::string;

#define COMPARE(a, b) \
    if ((a) != (b))  { \
        cerr << "failed:" << #a << " != " << #b << endl; \
        cerr << "\tgot:" << a << endl; \
        exit(1); \
    }

#define VERIFY(a) \
    if (!(a))  { \
        cerr << "failed:" << #a << endl; \
        exit(1); \
    }

void generate_points(int count, std::vector<SGVec3d>& vec)
{
    for (int i=0; i<count; ++i) {
        vec.push_back(SGVec3d(i * 0.5, i * i, i * 4));
    }
}

void generate_normals(int count, std::vector<SGVec3f>& vec)
{
    for (int i=0; i<count; ++i) {
        vec.push_back(normalize(SGVec3f(i, i * 2, i * -4)));
    }
}

void generate_tcs(int count, std::vector<SGVec2f>& vec)
{
    for (int i=0; i<count; ++i) {
        vec.push_back(SGVec2f(1.0 / i, 16.0 / i));
    }
}

void test_empty()
{
    SGBinObject empty;
    SGPath path(simgear::Dir::current().file("empty.btg.gz"));
    bool ok = empty.write_bin_file(path);
    VERIFY( ok );
    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);

   COMPARE(rd.get_wgs84_nodes().size(), 0);
   VERIFY(rd.get_pt_materials().empty());
}

void comparePoints(const SGBinObject& rd, const std::vector<SGVec3d>& b)
{
    for (unsigned int i=1; i<b.size(); i += 10) {
        SGVec3d pos = rd.get_wgs84_nodes()[i];
        pos += rd.get_gbs_center();

       if (!equivalent(pos, b[i], 0.1)) {
            cout << "i=" << i << endl;
            cout << b[i] << endl;
            cout << pos << endl;
        }

        VERIFY(equivalent(pos, b[i], 0.1));
    }
}

void compareTexCoords(const SGBinObject& rd, const std::vector<SGVec2f>& b)
{
    for (unsigned int i=1; i<b.size(); i += 10) {
        SGVec2f pos = rd.get_texcoords()[i];
        VERIFY(equivalent(pos, b[i], 0.001f));
    }
}

int_list make_tri(int maxIndex)
{
    int_list r;
    r.push_back(random() % maxIndex);
    r.push_back(random() % maxIndex);
    r.push_back(random() % maxIndex);
    return r;
}

void compareTris(const SGBinObject& a, const SGBinObject& b)
{
    unsigned int count = a.get_tri_materials().size();
    for (unsigned int i=0; i<count; i += 39) {
        const int_list& vA(a.get_tris_v()[i]);
        const int_list& vB(b.get_tris_v()[i]);
        VERIFY(vA == vB);

        COMPARE(a.get_tri_materials()[i], b.get_tri_materials()[i]);

        const int_list& tA(a.get_tris_tc()[i]);
        const int_list& tB(b.get_tris_tc()[i]);
        VERIFY(tA == tB);
    }
}

void generate_tris(SGBinObject& b, int count)
{
    group_list v, n, tc;
    string_list materials;

    int maxVertices = b.get_wgs84_nodes().size();
    int maxNormals = b.get_normals().size();
    int maxTCs = b.get_texcoords().size();

    for (int t=0; t<count; ++t) {
        v.push_back(make_tri(maxVertices));
        n.push_back(make_tri(maxNormals));
        tc.push_back(make_tri(maxTCs));
        materials.push_back("material1");
    }

    b.set_tris_v(v);
    b.set_tris_n(n);
    b.set_tris_tc(tc);
    b.set_tri_materials(materials);
}

void test_basic()
{
    SGBinObject basic;
    SGPath path(simgear::Dir::current().file("basic.btg.gz"));

    SGVec3d center(1, 2, 3);
    basic.set_gbs_center(center);
    basic.set_gbs_radius(12345);

    std::vector<SGVec3d> points;
    generate_points(1024, points);
    std::vector<SGVec3f> normals;
    generate_normals(1024, normals);
    std::vector<SGVec2f> texCoords;
    generate_tcs(10000, texCoords);

    basic.set_wgs84_nodes(points);
    basic.set_normals(normals);
    basic.set_texcoords(texCoords);

    bool ok = basic.write_bin_file(path);
    VERIFY( ok );

    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);
   COMPARE(rd.get_version(), 7); // should be version 7 since indices are < 2^16
   COMPARE(rd.get_gbs_center(), center);
   COMPARE(rd.get_gbs_radius(), 12345);
   COMPARE(rd.get_wgs84_nodes().size(), points.size());

   comparePoints(rd, points);
   compareTexCoords(rd, texCoords);
}

void test_many_tcs()
{
    SGBinObject basic;
    SGPath path(simgear::Dir::current().file("many_tex.btg.gz"));

    SGVec3d center(1, 2, 3);
    basic.set_gbs_center(center);
    basic.set_gbs_radius(12345);

    std::vector<SGVec3d> points;
    generate_points(10000, points);
    std::vector<SGVec3f> normals;
    generate_normals(1024, normals);
    std::vector<SGVec2f> texCoords;
    generate_tcs(100000, texCoords);

    basic.set_wgs84_nodes(points);
    basic.set_normals(normals);
    basic.set_texcoords(texCoords);

    generate_tris(basic, 20000);

    bool ok = basic.write_bin_file(path);
    VERIFY( ok );

    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);
   COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
   COMPARE(rd.get_wgs84_nodes().size(), points.size());
   COMPARE(rd.get_texcoords().size(), texCoords.size());

   comparePoints(rd, points);
   compareTexCoords(rd, texCoords);
   compareTris(basic, rd);
}

void test_big()
{
    SGBinObject basic;
    SGPath path(simgear::Dir::current().file("big.btg.gz"));

    SGVec3d center(1, 2, 3);
    basic.set_gbs_center(center);
    basic.set_gbs_radius(12345);

    std::vector<SGVec3d> points;
    generate_points(200000, points);
    std::vector<SGVec3f> normals;
    generate_normals(1024, normals);
    std::vector<SGVec2f> texCoords;
    generate_tcs(300000, texCoords);

    basic.set_wgs84_nodes(points);
    basic.set_normals(normals);
    basic.set_texcoords(texCoords);

    generate_tris(basic, 200000);

    bool ok = basic.write_bin_file(path);
    VERIFY( ok );

    SGBinObject rd;
   ok = rd.read_bin(path.str()) ;
   VERIFY( ok);
   COMPARE(rd.get_version(), 10); // should be version 10 since indices are > 2^16
   COMPARE(rd.get_wgs84_nodes().size(), points.size());
   COMPARE(rd.get_texcoords().size(), texCoords.size());

   comparePoints(rd, points);
   compareTexCoords(rd, texCoords);
   compareTris(basic, rd);
}

int main(int argc, char* argv[])
{
    test_empty();
    test_basic();
    test_many_tcs();
    test_big();

    return 0;
}
<|endoftext|>


<commit_before>
// -*- mode: c++; c-basic-offset:4 -*-

// This file is part of libdap, A C++ implementation of the OPeNDAP Data
// Access Protocol.

// Copyright (c) 2002,2003 OPeNDAP, Inc.
// Author: James Gallagher <jgallagher@opendap.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.

#include <cppunit/TextTestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/extensions/HelperMacros.h>

#include <strstream>
#include <string>

#include "Regex.h"

#include "Sequence.h"
#include "Int32.h"
#include "Str.h"
#include "debug.h"

using namespace CppUnit;
using namespace std;

//  Note: MS VC++ won't tolerate the embedded newlines in strings, hence the \n
//  is explicit.
static const char *s_as_string = \
"BaseType (0x.*):\n\
          _name: s\n\
          _type: 13\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0\n\
BaseType (0x.*):\n\
          _name: i1\n\
          _type: 4\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0x.*\n\
BaseType (0x.*):\n\
          _name: str1\n\
          _type: 8\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0x.*\n\
BaseType (0x.*):\n\
          _name: i2\n\
          _type: 4\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0x.*\n\
\n\
";

static Regex s_regex(s_as_string);

class SequenceTest : public TestFixture {
private:
    Sequence *s, *sss, *ts, *tts;

public:
    SequenceTest() {}
    ~SequenceTest() {}

    void setUp() {
	// Set up a simple sequence. Used to test ctor, assigment, et cetera.
	s = new Sequence("s");
	s->add_var(new Int32("i1"));
	s->add_var(new Str("str1"));
	s->add_var(new Int32("i2"));

	// Set up sss, used to test multi-level sequences
	sss = new Sequence("sss");
	sss->add_var(new Int32("i1"));

	ts = new Sequence("child_of_sss");
	ts->add_var(new Str("str1"));

	tts = new Sequence("child_of_child_of_sss");
	tts->add_var(new Int32("i2"));
	ts->add_var(tts);

	sss->add_var(ts);	// This has to be here because add_var adds
				// copies of its argument.
    }

    void tearDown() {
	delete s; s = 0;
	delete sss; sss = 0;
	delete ts; ts = 0;
	delete tts; tts = 0;
    }

    bool re_match(Regex &r, const char *s) {
	int match_position = r.match(s, strlen(s));
	DBG(cerr << "match position: " << match_position
	    << " string length: " << (int)strlen(s) << endl);
	return match_position == (int)strlen(s);
    }

    CPPUNIT_TEST_SUITE( SequenceTest );

    CPPUNIT_TEST(ctor_test);
    CPPUNIT_TEST(assignment);
    CPPUNIT_TEST(copy_ctor);

    CPPUNIT_TEST_SUITE_END();

    // Tests for methods
    void ctor_test() {
	DBG(cerr << "s: " << s->toString() << endl);
	CPPUNIT_ASSERT(re_match(s_regex, s->toString().c_str()));
    }

    void assignment() {
	Sequence ts2;

	ts2 = *s;
	DBG(cerr << "ts2: " << ts2.toString() << endl);
	CPPUNIT_ASSERT(re_match(s_regex, ts2.toString().c_str()));
    }

    void copy_ctor() {
	Sequence s2 = *s;
	CPPUNIT_ASSERT(re_match(s_regex, s2.toString().c_str()));
    }
};

CPPUNIT_TEST_SUITE_REGISTRATION(SequenceTest);

int
main( int argc, char* argv[] )
{
    CppUnit::TextTestRunner runner;
    runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );

    runner.run();

    return 0;
}
<commit_msg>Changed s_as_string so that it uses the correct value for _type for 's.' This value changed since I got rid of List.<commit_after>
// -*- mode: c++; c-basic-offset:4 -*-

// This file is part of libdap, A C++ implementation of the OPeNDAP Data
// Access Protocol.

// Copyright (c) 2002,2003 OPeNDAP, Inc.
// Author: James Gallagher <jgallagher@opendap.org>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.

#include <cppunit/TextTestRunner.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/extensions/HelperMacros.h>

#include <string>

#include "Regex.h"

#include "Sequence.h"
#include "Int32.h"
#include "Str.h"
#include "debug.h"

using namespace CppUnit;
using namespace std;

//  Note: MS VC++ won't tolerate the embedded newlines in strings, hence the \n
//  is explicit.
static const char *s_as_string = \
"BaseType (0x.*):\n\
          _name: s\n\
          _type: 12\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0\n\
BaseType (0x.*):\n\
          _name: i1\n\
          _type: 4\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0x.*\n\
BaseType (0x.*):\n\
          _name: str1\n\
          _type: 8\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0x.*\n\
BaseType (0x.*):\n\
          _name: i2\n\
          _type: 4\n\
          _read_p: 0\n\
          _send_p: 0\n\
          _synthesized_p: 0\n\
          d_parent: 0x.*\n\
\n\
";

static Regex s_regex(s_as_string);

class SequenceTest : public TestFixture {
private:
    Sequence *s, *sss, *ts, *tts;

public:
    SequenceTest() {}
    ~SequenceTest() {}

    void setUp() {
	// Set up a simple sequence. Used to test ctor, assigment, et cetera.
	s = new Sequence("s");
	s->add_var(new Int32("i1"));
	s->add_var(new Str("str1"));
	s->add_var(new Int32("i2"));

	// Set up sss, used to test multi-level sequences
	sss = new Sequence("sss");
	sss->add_var(new Int32("i1"));

	ts = new Sequence("child_of_sss");
	ts->add_var(new Str("str1"));

	tts = new Sequence("child_of_child_of_sss");
	tts->add_var(new Int32("i2"));
	ts->add_var(tts);

	sss->add_var(ts);	// This has to be here because add_var adds
				// copies of its argument.
    }

    void tearDown() {
	delete s; s = 0;
	delete sss; sss = 0;
	delete ts; ts = 0;
	delete tts; tts = 0;
    }

    bool re_match(Regex &r, const char *s) {
	int match_position = r.match(s, strlen(s));
	DBG(cerr << "match position: " << match_position
	    << " string length: " << (int)strlen(s) << endl);
	return match_position == (int)strlen(s);
    }

    CPPUNIT_TEST_SUITE( SequenceTest );

    CPPUNIT_TEST(ctor_test);
    CPPUNIT_TEST(assignment);
    CPPUNIT_TEST(copy_ctor);

    CPPUNIT_TEST_SUITE_END();

    // Tests for methods
    void ctor_test() {
	DBG(cerr << "s: " << s->toString() << endl);
	CPPUNIT_ASSERT(re_match(s_regex, s->toString().c_str()));
    }

    void assignment() {
	Sequence ts2;

	ts2 = *s;
	DBG(cerr << "ts2: " << ts2.toString() << endl);
	CPPUNIT_ASSERT(re_match(s_regex, ts2.toString().c_str()));
    }

    void copy_ctor() {
	Sequence s2 = *s;
	CPPUNIT_ASSERT(re_match(s_regex, s2.toString().c_str()));
    }
};

CPPUNIT_TEST_SUITE_REGISTRATION(SequenceTest);

int
main( int argc, char* argv[] )
{
    CppUnit::TextTestRunner runner;
    runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() );

    runner.run();

    return 0;
}
<|endoftext|>


<commit_before>/*

Copyright 2015-2022 Igor Petrovic

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

#include "core/src/util/Util.h"
#include "core/src/Timing.h"
#include "hardware/i2c.h"

namespace
{
    i2c_inst* _i2cInstance[CORE_MCU_MAX_I2C_INTERFACES] = {
        i2c0,
        i2c1
    };
}    // namespace

namespace core::mcu::i2c
{
    bool init(core::mcu::io::pin_t sda, core::mcu::io::pin_t scl, uint8_t channel, uint32_t clockSpeed)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        i2c_init(_i2cInstance[channel], clockSpeed);
        gpio_set_function(sda.index, GPIO_FUNC_I2C);
        gpio_set_function(scl.index, GPIO_FUNC_I2C);
        gpio_pull_up(sda.index);
        gpio_pull_up(scl.index);

        return true;
    }

    bool deInit(uint8_t channel)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        i2c_deinit(_i2cInstance[channel]);
        return true;
    }

    bool write(uint8_t channel, uint8_t address, uint8_t* buffer, size_t size)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        return i2c_write_blocking(_i2cInstance[channel],
                                  address,
                                  buffer,
                                  size,
                                  false) > 0;

        return false;
    }

    bool deviceAvailable(uint8_t channel, uint8_t address)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        uint8_t dummy = 0;

        return i2c_read_blocking(_i2cInstance[channel],
                                 address,
                                 &dummy,
                                 1,
                                 false) >= 0;
    }
}    // namespace core::mcu::i2c<commit_msg>improve i2c support for rp2040<commit_after>/*

Copyright 2015-2022 Igor Petrovic

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

#include "core/src/util/Util.h"
#include "core/src/Timing.h"
#include "hardware/i2c.h"

namespace
{
    // Match SDA/SCL pin indexes with I2C interface:
    // RP2040 uses semi-flexible pin mapping for I2C -
    // specific pins use specific I2C interface
    i2c_inst* i2cInstance(uint8_t sda, uint8_t scl)
    {
        if ((sda == 0) && (scl == 1))
        {
            return i2c0;
        }

        if ((sda == 2) && (scl == 3))
        {
            return i2c1;
        }

        if ((sda == 4) && (scl == 5))
        {
            return i2c0;
        }

        if ((sda == 6) && (scl == 7))
        {
            return i2c1;
        }

        if ((sda == 8) && (scl == 9))
        {
            return i2c0;
        }

        if ((sda == 10) && (scl == 11))
        {
            return i2c1;
        }

        if ((sda == 12) && (scl == 13))
        {
            return i2c0;
        }

        if ((sda == 14) && (scl == 15))
        {
            return i2c1;
        }

        if ((sda == 26) && (scl == 27))
        {
            return i2c1;
        }

        if ((sda == 20) && (scl == 21))
        {
            return i2c0;
        }

        if ((sda == 18) && (scl == 19))
        {
            return i2c1;
        }

        if ((sda == 16) && (scl == 17))
        {
            return i2c0;
        }

        return nullptr;
    }

    // Array holding I2C interface for specific channel:
    // channel is specified by user, so it's possible to init I2C by
    // specifying "init I2C channel 0 with the following pins"
    // Each pin group matches with specific I2C interface.
    // Instance should be set in init().
    i2c_inst* _i2cInstanceMatched[CORE_MCU_MAX_I2C_INTERFACES] = {
        nullptr,
        nullptr,
    };
}    // namespace

namespace core::mcu::i2c
{
    bool init(core::mcu::io::pin_t sda, core::mcu::io::pin_t scl, uint8_t channel, uint32_t clockSpeed)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        auto instance                = i2cInstance(sda.index, scl.index);
        _i2cInstanceMatched[channel] = instance;

        i2c_init(_i2cInstanceMatched[channel], clockSpeed);
        gpio_set_function(sda.index, GPIO_FUNC_I2C);
        gpio_set_function(scl.index, GPIO_FUNC_I2C);
        gpio_pull_up(sda.index);
        gpio_pull_up(scl.index);

        return true;
    }

    bool deInit(uint8_t channel)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        i2c_deinit(_i2cInstanceMatched[channel]);

        return true;
    }

    bool write(uint8_t channel, uint8_t address, uint8_t* buffer, size_t size)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        return i2c_write_blocking(_i2cInstanceMatched[channel],
                                  address,
                                  buffer,
                                  size,
                                  false) > 0;

        return false;
    }

    bool deviceAvailable(uint8_t channel, uint8_t address)
    {
        if (channel >= CORE_MCU_MAX_I2C_INTERFACES)
        {
            return false;
        }

        uint8_t dummy = 0;

        return i2c_read_blocking(_i2cInstanceMatched[channel],
                                 address,
                                 &dummy,
                                 1,
                                 false) >= 0;
    }
}    // namespace core::mcu::i2c<|endoftext|>


<commit_before>// __BEGIN_LICENSE__
// Copyright (C) 2006-2011 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

/// \file disparitydebug.cc
///

#ifdef _MSC_VER
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4996)
#endif

#ifdef NDEBUG
#undef NDEBUG
#endif

#include <stdlib.h>

#include <vw/FileIO.h>
#include <vw/Image.h>
#include <vw/Stereo/DisparityMap.h>
#include <vw/tools/Common.h>
#include <asp/Core/Macros.h>
#include <asp/Core/Common.h>
using namespace vw;
using namespace vw::stereo;

namespace po = boost::program_options;
namespace fs = boost::filesystem;

struct Options : asp::BaseOptions {
  // Input
  std::string input_file_name;

  // Output
  std::string output_prefix, output_file_type;
};

void handle_arguments( int argc, char *argv[], Options& opt ) {
  po::options_description general_options("");
  general_options.add_options()
    ("output-prefix,o", po::value(&opt.output_prefix), "Specify the output prefix")
    ("output-filetype,t", po::value(&opt.output_file_type)->default_value("tif"), "Specify the output file");
  general_options.add( asp::BaseOptionsDescription(opt) );

  po::options_description positional("");
  positional.add_options()
    ("input-file", po::value(&opt.input_file_name), "Input disparity map");

  po::positional_options_description positional_desc;
  positional_desc.add("input-file", 1);

  std::ostringstream usage;
  usage << "Usage: " << argv[0] << " [options] <input disparity map> \n";

  po::variables_map vm =
    asp::check_command_line( argc, argv, opt, general_options,
                             positional, positional_desc, usage.str() );

  if ( opt.input_file_name.empty() )
    vw_throw( ArgumentErr() << "Missing input file!\n"
              << usage.str() << general_options );
  if ( opt.output_prefix.empty() )
    opt.output_prefix = fs::path(opt.input_file_name).stem();
}

template <class PixelT>
void do_disparity_visualization(Options& opt) {
  DiskImageView<PixelT > disk_disparity_map(opt.input_file_name);

  vw_out() << "\t--> Computing disparity range \n";
  BBox2 disp_range = get_disparity_range(disk_disparity_map);
  vw_out() << "\t    Horizontal - [" << disp_range.min().x()
           << " " << disp_range.max().x() << "]    Vertical: ["
           << disp_range.min().y() << " " << disp_range.max().y() << "]\n";

  typedef typename PixelChannelType<PixelT>::type ChannelT;
  ImageViewRef<ChannelT> horizontal = apply_mask(copy_mask(clamp(normalize(select_channel(disk_disparity_map,0), disp_range.min().x(), disp_range.max().x())),disk_disparity_map));
  ImageViewRef<ChannelT> vertical = apply_mask(copy_mask(clamp(normalize(select_channel(disk_disparity_map,1), disp_range.min().y(), disp_range.max().y())),disk_disparity_map));

  vw_out() << "\t--> Saving disparity debug images\n";
  block_write_gdal_image( opt.output_prefix+"-H."+opt.output_file_type,
                          channel_cast_rescale<uint8>(horizontal),
                          opt, TerminalProgressCallback("asp","\t    Left  : "));
  block_write_gdal_image( opt.output_prefix + "-V." + opt.output_file_type,
                          channel_cast_rescale<uint8>(vertical),
                          opt, TerminalProgressCallback("asp","\t    Right : "));
}

int main( int argc, char *argv[] ) {

  Options opt;
  try {
    handle_arguments( argc, argv, opt );

    vw_out() << "Opening " << opt.input_file_name << "\n";
    ImageFormat fmt = tools::taste_image(opt.input_file_name);

    std::cout << "Pixel format: "
              << pixel_format_name(fmt.pixel_format) << "\n";

    switch(fmt.pixel_format) {
    case VW_PIXEL_GENERIC_2_CHANNEL:
      switch (fmt.channel_type) {
      case VW_CHANNEL_INT32:
        do_disparity_visualization<Vector2i>(opt); break;
      default:
        do_disparity_visualization<Vector2f>(opt); break;
      } break;
    case VW_PIXEL_RGB:
    case VW_PIXEL_GENERIC_3_CHANNEL:
      switch (fmt.channel_type) {
      case VW_CHANNEL_INT32:
        do_disparity_visualization<PixelMask<Vector2i> >(opt); break;
      default:
        do_disparity_visualization<PixelMask<Vector2f> >(opt); break;
      } break;
    default:
      vw_throw( ArgumentErr() << "Unsupported pixel format. Expected GENERIC 2 or 3 CHANNEL image." );
    }

  } ASP_STANDARD_CATCHES;

  return 0;
}
<commit_msg>Remove accidental debug code that made it into repo<commit_after>// __BEGIN_LICENSE__
// Copyright (C) 2006-2011 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

/// \file disparitydebug.cc
///

#ifdef _MSC_VER
#pragma warning(disable:4244)
#pragma warning(disable:4267)
#pragma warning(disable:4996)
#endif

#ifdef NDEBUG
#undef NDEBUG
#endif

#include <stdlib.h>

#include <vw/FileIO.h>
#include <vw/Image.h>
#include <vw/Stereo/DisparityMap.h>
#include <vw/tools/Common.h>
#include <asp/Core/Macros.h>
#include <asp/Core/Common.h>
using namespace vw;
using namespace vw::stereo;

namespace po = boost::program_options;
namespace fs = boost::filesystem;

struct Options : asp::BaseOptions {
  // Input
  std::string input_file_name;

  // Output
  std::string output_prefix, output_file_type;
};

void handle_arguments( int argc, char *argv[], Options& opt ) {
  po::options_description general_options("");
  general_options.add_options()
    ("output-prefix,o", po::value(&opt.output_prefix), "Specify the output prefix")
    ("output-filetype,t", po::value(&opt.output_file_type)->default_value("tif"), "Specify the output file");
  general_options.add( asp::BaseOptionsDescription(opt) );

  po::options_description positional("");
  positional.add_options()
    ("input-file", po::value(&opt.input_file_name), "Input disparity map");

  po::positional_options_description positional_desc;
  positional_desc.add("input-file", 1);

  std::ostringstream usage;
  usage << "Usage: " << argv[0] << " [options] <input disparity map> \n";

  po::variables_map vm =
    asp::check_command_line( argc, argv, opt, general_options,
                             positional, positional_desc, usage.str() );

  if ( opt.input_file_name.empty() )
    vw_throw( ArgumentErr() << "Missing input file!\n"
              << usage.str() << general_options );
  if ( opt.output_prefix.empty() )
    opt.output_prefix = fs::path(opt.input_file_name).stem();
}

template <class PixelT>
void do_disparity_visualization(Options& opt) {
  DiskImageView<PixelT > disk_disparity_map(opt.input_file_name);

  vw_out() << "\t--> Computing disparity range \n";
  BBox2 disp_range = get_disparity_range(disk_disparity_map);
  vw_out() << "\t    Horizontal - [" << disp_range.min().x()
           << " " << disp_range.max().x() << "]    Vertical: ["
           << disp_range.min().y() << " " << disp_range.max().y() << "]\n";

  typedef typename PixelChannelType<PixelT>::type ChannelT;
  ImageViewRef<ChannelT> horizontal = apply_mask(copy_mask(clamp(normalize(select_channel(disk_disparity_map,0), disp_range.min().x(), disp_range.max().x())),disk_disparity_map));
  ImageViewRef<ChannelT> vertical = apply_mask(copy_mask(clamp(normalize(select_channel(disk_disparity_map,1), disp_range.min().y(), disp_range.max().y())),disk_disparity_map));

  vw_out() << "\t--> Saving disparity debug images\n";
  block_write_gdal_image( opt.output_prefix+"-H."+opt.output_file_type,
                          channel_cast_rescale<uint8>(horizontal),
                          opt, TerminalProgressCallback("asp","\t    Left  : "));
  block_write_gdal_image( opt.output_prefix + "-V." + opt.output_file_type,
                          channel_cast_rescale<uint8>(vertical),
                          opt, TerminalProgressCallback("asp","\t    Right : "));
}

int main( int argc, char *argv[] ) {

  Options opt;
  try {
    handle_arguments( argc, argv, opt );

    vw_out() << "Opening " << opt.input_file_name << "\n";
    ImageFormat fmt = tools::taste_image(opt.input_file_name);

    switch(fmt.pixel_format) {
    case VW_PIXEL_GENERIC_2_CHANNEL:
      switch (fmt.channel_type) {
      case VW_CHANNEL_INT32:
        do_disparity_visualization<Vector2i>(opt); break;
      default:
        do_disparity_visualization<Vector2f>(opt); break;
      } break;
    case VW_PIXEL_RGB:
    case VW_PIXEL_GENERIC_3_CHANNEL:
      switch (fmt.channel_type) {
      case VW_CHANNEL_INT32:
        do_disparity_visualization<PixelMask<Vector2i> >(opt); break;
      default:
        do_disparity_visualization<PixelMask<Vector2f> >(opt); break;
      } break;
    default:
      vw_throw( ArgumentErr() << "Unsupported pixel format. Expected GENERIC 2 or 3 CHANNEL image." );
    }

  } ASP_STANDARD_CATCHES;

  return 0;
}
<|endoftext|>