| /* |
| * Copyright 2014 Google Inc. |
| * |
| * 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. |
| */ |
| |
| // Author: Victor Chudnovsky |
| |
| // 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_ |