text: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.
   */
  
  
  // This file defines the MultipleFrame API for reading
  // and writing static and animated images.
  
  #ifndef PAGESPEED_KERNEL_IMAGE_IMAGE_FRAME_INTERFACE_H_
  #define PAGESPEED_KERNEL_IMAGE_IMAGE_FRAME_INTERFACE_H_
  
  #include <cstddef>
  #include "pagespeed/kernel/base/basictypes.h"
  #include "pagespeed/kernel/base/string.h"
  #include "pagespeed/kernel/image/image_util.h"
  #include "pagespeed/kernel/image/scanline_status.h"
  
  namespace net_instaweb {
  class MessageHandler;
  }
  
  namespace pagespeed {
  
  namespace image_compression {
  
  using net_instaweb::MessageHandler;
  
  struct FrameSpec;
  
  struct ImageSpec {
    ImageSpec();
    void Reset();
  
    size_px width;
    size_px height;
    size_px num_frames;
  
    // This is the total number of times to loop through all the frames
    // (NOT the *repeat* number).
    unsigned int loop_count;
  
    PixelRgbaChannels bg_color;
    bool use_bg_color;
  
    // Whether the image size was adjusted (as can happen when
    // implementing some quirks modes).
    bool image_size_adjusted;
  
    // Returns x truncated to a valid column index in [0, width]. Note
    // that a value of 'width' denotes the first invalid index.
    size_px TruncateXIndex(size_px x) const;
  
    // Returns y truncated to a valid row index in [0, height]. Note
    // that a value of 'height' denotes the first invalid index.
    size_px TruncateYIndex(size_px y) const;
  
    // Returns true iff frame_spec fits entirely within this ImageSpec.
    bool CanContainFrame(const FrameSpec& frame_spec) const;
  
    GoogleString ToString() const;
    bool Equals(const ImageSpec& other) const;
  };
  
  // FrameSpec must fit entirely within its image; in other words
  // ImageSpec::CanContainFrame(frame_spec) should return true.
  struct FrameSpec {
    enum DisposalMethod {
      DISPOSAL_UNKNOWN = 0,
      DISPOSAL_NONE,
      DISPOSAL_BACKGROUND,
  
      // TODO(vchudnov): May not be supported by WebP. If that's the
      // case, treat as DISPOSAL_BACKGROUND instead.
      DISPOSAL_RESTORE
    };
  
    FrameSpec();
    void Reset();
  
    size_px width;
    size_px height;
    size_px top;
    size_px left;
    PixelFormat pixel_format;
    size_t duration_ms;
    DisposalMethod disposal;
  
    // Whether this frame was progressively encoded by the origin site, so that
    // it could begin to be rendered even before the entire image was
    // transferred. This does not affect the data format passed in or out of
    // this API, but provides a hint that API clients may choose to take.
    bool hint_progressive;
  
    GoogleString ToString() const;
    bool Equals(const FrameSpec& other) const;
  };
  
  #if defined(IF_OK_RUN)
  #error "Preprocessor macro collision."
  #endif
  
  // Boilerplate for the convenience methods below. This code snippet
  // invokes _CALL only if _STATUS does not indicate a pre-existing
  // error, and returns a bool indicating success or failure of the
  // preceding state as well as _CALL, if invoked. Note that this
  // snippet returns from the function in which it is embedded.
  #define IF_OK_RUN(_STATUS, _CALL)                               \
    if (!_STATUS->Success()) {                                    \
      return false;                                               \
    }                                                             \
    *_STATUS = _CALL;                                             \
    return _STATUS->Success();
  
  // Interface for reading both animated and static images.
  //
  // Typical usage of this API is as follows:
  //
  //   Initialize()
  //   GetImageSpec()  // optional
  //   while (HasMoreFrames()) {
  //     PrepareNextFrame()
  //     GetFrameSpec()  // optional
  //     while (HasMoreScanlines()) {
  //       ReadNextScanline()
  //     }
  //   }
  //
  // The "optional" lines above are not necessary for just reading an
  // image. You might want to call them, though, if the metadata they
  // contain is of interest, or if you're passing the read image to a
  // MultipleFrameWriter, which needs the metadata in ImageSpec and
  // FrameSpec (see below).
  class MultipleFrameReader {
   public:
    explicit MultipleFrameReader(MessageHandler* handler);
    virtual ~MultipleFrameReader();
  
    // Resets the MultipleFrameReader to its initial state.
    virtual ScanlineStatus Reset() = 0;
  
    // Initializes MultipleFrameReader to read image data of length
    // 'buffer_length_' from 'image_buffer_'. This function should take
    // care of calling Reset() if necessary, so that the sequence
    // "Reset(); Initialize();" is never needed.
    virtual ScanlineStatus Initialize() = 0;
  
    // Sets 'buffer_length_' and 'image_buffer_' and calls
    // Initialize(). Do not override this function; override the no-arg
    // overload instead.
    ScanlineStatus Initialize(const void* image_buffer, size_t buffer_length) {
      image_buffer_ = image_buffer;
      buffer_length_ = buffer_length;
      return Initialize();
    }
  
    // Returns true iff the image being read has additional frames beyond
    // the current frame being read. For any well-formed image with at
    // least one frame (or for a well-formed static image), this will
    // return true before the first call to PrepareNextFrame().
    virtual bool HasMoreFrames() const = 0;
  
    // Returns true iff the current frame has more scanlines that have
    // not yet been read.
    virtual bool HasMoreScanlines() const = 0;
  
    // Prepares to read scanlines from the frame after the current
    // one. Must be called before reading from the first frame.
    virtual ScanlineStatus PrepareNextFrame() = 0;
  
    // Reads the next available scanline in the current frame and copies
    // a pointer to it into '*out_scanline_bytes'. This class retains
    // ownership of the read scanline. The scanline encodes as many
    // pixels as the width of the current frame, which is not
    // necessarily the width of the whole image.
    virtual ScanlineStatus ReadNextScanline(const void** out_scanline_bytes) = 0;
  
    // Assigns to '*frame_spec' the FrameSpec describing
    // the current frame.
    //
    // TODO(vchudnov): Consider simplifying this method to return
    // frame_spec rather than the ScanlineStatus.
    virtual ScanlineStatus GetFrameSpec(FrameSpec* frame_spec) const = 0;
  
    // Copies into '*image_spec' the ImageSpec describing
    // the image.
    virtual ScanlineStatus GetImageSpec(ImageSpec* image_spec) const = 0;
  
    // The message handler used by this class. Neither the caller nor
    // this class have ownership.
    MessageHandler* message_handler() const {
      return message_handler_;
    }
  
    virtual ScanlineStatus set_quirks_mode(QuirksMode quirks_mode) {
      quirks_mode_ = quirks_mode;
      return ScanlineStatus(SCANLINE_STATUS_SUCCESS);
    }
  
    virtual QuirksMode quirks_mode() const {
      return quirks_mode_;
    }
  
    // Convenience forms of the methods above. If 'status' indicates an
    // error on entry, each of these methods does nothing and returns
    // false. Otherwise, it calls the corresponding method above,
    // updates 'status', and returns true iff the call succeeded.
  
    bool Reset(ScanlineStatus* status) {
      IF_OK_RUN(status, Reset());
    }
    bool Initialize(const void* image_buffer, size_t buffer_length,
                    ScanlineStatus* status) {
      IF_OK_RUN(status, Initialize(image_buffer, buffer_length));
    }
    bool Initialize(ScanlineStatus* status) {
      IF_OK_RUN(status, Initialize());
    }
    bool PrepareNextFrame(ScanlineStatus* status) {
      IF_OK_RUN(status, PrepareNextFrame());
    }
    bool ReadNextScanline(const void** out_scanline_bytes,
                          ScanlineStatus* status) {
      IF_OK_RUN(status, ReadNextScanline(out_scanline_bytes));
    }
    bool GetFrameSpec(FrameSpec* frame_spec,
                      ScanlineStatus* status) {
      IF_OK_RUN(status, GetFrameSpec(frame_spec));
    }
    bool GetImageSpec(ImageSpec* image_spec,
                      ScanlineStatus* status) {
      IF_OK_RUN(status, GetImageSpec(image_spec));
    }
  
    bool set_quirks_mode(QuirksMode quirks_mode,
                         ScanlineStatus* status) {
      IF_OK_RUN(status, set_quirks_mode(quirks_mode));
    }
  
   protected:
    const void* image_buffer_;
    size_t buffer_length_;
  
   private:
    // Handles logging info, warning, and error messages.
    MessageHandler* message_handler_;
  
    // The browser quirks mode to implement, if any.
    QuirksMode quirks_mode_;
  
    DISALLOW_COPY_AND_ASSIGN(MultipleFrameReader);
  };
  
  
  // Interface for writing both animated and static images.
  //
  // Typical usage of this API is as follows:
  //   Initialize();
  //   PrepareImage();
  //   while (have_frame) {
  //     PrepareNextFrame();
  //     while (have_scanline) {
  //         WriteNextScanline();
  //     }
  //   }
  //   FinalizeWrite();
  class MultipleFrameWriter {
   public:
    explicit MultipleFrameWriter(MessageHandler* handler);
    virtual ~MultipleFrameWriter();
  
    // Initializes the writer to use the format-specific configuration
    // in 'config' to write an image to 'out'. It is up to the client to
    // ensure 'config' points to data of the right type for the given
    // child class it invokes.
    virtual ScanlineStatus Initialize(const void* config,
                                      GoogleString* out) = 0;
  
    // Prepares to write an image with the characteristics in
    // 'image_spec'.
    virtual ScanlineStatus PrepareImage(const ImageSpec* image_spec) = 0;
  
    // Prepares to write scanlines to the frame after the current one by
    // setting its properties to 'frame_spec'. Must be called before
    // writing to the first frame.
    virtual ScanlineStatus PrepareNextFrame(const FrameSpec* frame_spec) = 0;
  
    // Writes 'scanline_bytes' to the next scanline of the current
    // frame. The scanline's width in pixels and pixel format (and thus,
    // its width in bytes) are as set in the preceding call to
    // PrepareNextFrame(); that many bytes from '*scanline_bytes' are
    // read.
    virtual ScanlineStatus WriteNextScanline(const void *scanline_bytes) = 0;
  
    // Finalizes the image once all the frames have been written.
    virtual ScanlineStatus FinalizeWrite() = 0;
  
    // The message handler used by this class. Neither the caller nor
    // this class have ownership.
    MessageHandler* message_handler() const {
      return message_handler_;
    }
  
    // Convenience forms of the methods above. If 'status' indicates an
    // error on entry, each of these methods does nothing and returns
    // false. Otherwise, it calls the corresponding method above,
    // updates 'status', and returns true iff the call succeeded.
  
    bool Initialize(const void* config, GoogleString* out,
                    ScanlineStatus* status) {
      IF_OK_RUN(status, Initialize(config, out));
    }
    bool PrepareImage(const ImageSpec* image_spec, ScanlineStatus* status) {
      IF_OK_RUN(status, PrepareImage(image_spec));
    }
    bool PrepareNextFrame(const FrameSpec* frame_spec, ScanlineStatus* status) {
      IF_OK_RUN(status, PrepareNextFrame(frame_spec));
    }
    bool WriteNextScanline(const void* scanline_bytes, ScanlineStatus* status) {
      IF_OK_RUN(status, WriteNextScanline(scanline_bytes));
    }
    bool FinalizeWrite(ScanlineStatus* status) {
      IF_OK_RUN(status, FinalizeWrite());
    }
  
   private:
    // Handles logging info, warning, and error messages.
    MessageHandler* message_handler_;
  
    DISALLOW_COPY_AND_ASSIGN(MultipleFrameWriter);
  };
  
  #undef IF_OK_RUN
  
  }  // namespace image_compression
  
  }  // namespace pagespeed
  
  #endif  // PAGESPEED_KERNEL_IMAGE_IMAGE_FRAME_INTERFACE_H_
  