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

#ifndef SkCodec_DEFINED
#define SkCodec_DEFINED

#include "SkEncodedFormat.h"
#include "SkImageGenerator.h"
#include "SkImageInfo.h"
#include "SkScanlineDecoder.h"
#include "SkSize.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkTypes.h"

class SkData;

/**
 *  Abstraction layer directly on top of an image codec.
 */
class SkCodec : public SkImageGenerator {
public:
    /**
     *  If this stream represents an encoded image that we know how to decode,
     *  return an SkCodec that can decode it. Otherwise return NULL.
     *
     *  If NULL is returned, the stream is deleted immediately. Otherwise, the
     *  SkCodec takes ownership of it, and will delete it when done with it.
     */
    static SkCodec* NewFromStream(SkStream*);

    /**
     *  If this data represents an encoded image that we know how to decode,
     *  return an SkCodec that can decode it. Otherwise return NULL.
     *
     *  Will take a ref if it returns a codec, else will not affect the data.
     */
    static SkCodec* NewFromData(SkData*);

    /**
     *  Return a size that approximately supports the desired scale factor.
     *  The codec may not be able to scale efficiently to the exact scale
     *  factor requested, so return a size that approximates that scale.
     *
     *  FIXME: Move to SkImageGenerator?
     */
    SkISize getScaledDimensions(float desiredScale) const {
        return this->onGetScaledDimensions(desiredScale);
    }

    /**
     *  Format of the encoded data.
     */
    SkEncodedFormat getEncodedFormat() const { return this->onGetEncodedFormat(); }

    /**
     *  Return an object which can be used to decode individual scanlines.
     *
     *  This object is owned by the SkCodec, which will handle its lifetime. The
     *  returned object is only valid until the SkCodec is deleted or the next
     *  call to getScanlineDecoder, whichever comes first.
     *
     *  Calling a second time will rewind and replace the existing one with a
     *  new one. If the stream cannot be rewound, this will delete the existing
     *  one and return NULL.
     *
     *  @param dstInfo Info of the destination. If the dimensions do not match
     *      those of getInfo, this implies a scale.
     *  @param options Contains decoding options, including if memory is zero
     *      initialized.
     *  @param ctable A pointer to a color table.  When dstInfo.colorType() is
     *      kIndex8, this should be non-NULL and have enough storage for 256
     *      colors.  The color table will be populated after decoding the palette.
     *  @param ctableCount A pointer to the size of the color table.  When
     *      dstInfo.colorType() is kIndex8, this should be non-NULL.  It will
     *      be modified to the true size of the color table (<= 256) after
     *      decoding the palette.
     *  @return New SkScanlineDecoder, or NULL on failure.
     *
     *  NOTE: If any rows were previously decoded, this requires rewinding the
     *  SkStream.
     *
     *  NOTE: The scanline decoder is owned by the SkCodec and will delete it
     *  when the SkCodec is deleted.
     */
    SkScanlineDecoder* getScanlineDecoder(const SkImageInfo& dstInfo, const Options* options,
                                          SkPMColor ctable[], int* ctableCount);

    /**
     *  Simplified version of getScanlineDecoder() that asserts that info is NOT
     *  kIndex8_SkColorType and uses the default Options.
     */
    SkScanlineDecoder* getScanlineDecoder(const SkImageInfo& dstInfo);

    /**
     *  Some images may initially report that they have alpha due to the format
     *  of the encoded data, but then never use any colors which have alpha
     *  less than 100%. This function can be called *after* decoding to
     *  determine if such an image truly had alpha. Calling it before decoding
     *  is undefined.
     *  FIXME: see skbug.com/3582.
     */
    bool reallyHasAlpha() const {
        return this->onReallyHasAlpha();
    }

protected:
    SkCodec(const SkImageInfo&, SkStream*);

    virtual SkISize onGetScaledDimensions(float /* desiredScale */) const {
        // By default, scaling is not supported.
        return this->getInfo().dimensions();
    }

    virtual SkEncodedFormat onGetEncodedFormat() const = 0;

    /**
     *  Override if your codec supports scanline decoding.
     *
     *  As in onGetPixels(), the implementation must call rewindIfNeeded() and
     *  handle as appropriate.
     *
     *  @param dstInfo Info of the destination. If the dimensions do not match
     *      those of getInfo, this implies a scale.
     *  @param options Contains decoding options, including if memory is zero
     *      initialized.
     *  @param ctable A pointer to a color table.  When dstInfo.colorType() is
     *      kIndex8, this should be non-NULL and have enough storage for 256
     *      colors.  The color table will be populated after decoding the palette.
     *  @param ctableCount A pointer to the size of the color table.  When
     *      dstInfo.colorType() is kIndex8, this should be non-NULL.  It will
     *      be modified to the true size of the color table (<= 256) after
     *      decoding the palette.
     *  @return New SkScanlineDecoder on success, NULL otherwise. The SkCodec
     *      will take ownership of the returned scanline decoder.
     */
    virtual SkScanlineDecoder* onGetScanlineDecoder(const SkImageInfo& dstInfo,
                                                    const Options& options,
                                                    SkPMColor ctable[],
                                                    int* ctableCount) {
        return NULL;
    }

    virtual bool onReallyHasAlpha() const { return false; }

    enum RewindState {
        kRewound_RewindState,
        kNoRewindNecessary_RewindState,
        kCouldNotRewind_RewindState
    };
    /**
     *  If the stream was previously read, attempt to rewind.
     *  @returns:
     *      kRewound if the stream needed to be rewound, and the
     *               rewind succeeded.
     *      kNoRewindNecessary if the stream did not need to be
     *                         rewound.
     *      kCouldNotRewind if the stream needed to be rewound, and
     *                      rewind failed.
     *
     *  Subclasses MUST call this function before reading the stream (e.g. in
     *  onGetPixels). If it returns false, onGetPixels should return
     *  kCouldNotRewind.
     */
    RewindState SK_WARN_UNUSED_RESULT rewindIfNeeded();

    /*
     *
     * Get method for the input stream
     *
     */
    SkStream* stream() {
        return fStream.get();
    }

private:
    SkAutoTDelete<SkStream>             fStream;
    bool                                fNeedsRewind;
    SkAutoTDelete<SkScanlineDecoder>    fScanlineDecoder;

    typedef SkImageGenerator INHERITED;
};
#endif // SkCodec_DEFINED