From 33968813533b4a46307f2146acc652309ace7308 Mon Sep 17 00:00:00 2001 From: Cameron Kaiser Date: Fri, 8 Feb 2019 19:52:20 -0800 Subject: [PATCH] #501: functional decoder wrapper --- image/decoders/nsWEBPDecoder.cpp | 195 +++++++++++++++++++++++++++++++ image/decoders/nsWEBPDecoder.h | 78 +++++++++++++ 2 files changed, 273 insertions(+) create mode 100644 image/decoders/nsWEBPDecoder.cpp create mode 100644 image/decoders/nsWEBPDecoder.h diff --git a/image/decoders/nsWEBPDecoder.cpp b/image/decoders/nsWEBPDecoder.cpp new file mode 100644 index 000000000..c502f9038 --- /dev/null +++ b/image/decoders/nsWEBPDecoder.cpp @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* Contributor(s): + * Vikas Arora */ + +#include "nsWEBPDecoder.h" + +#include "ImageLogging.h" +#include "gfxColor.h" +#include "gfxPlatform.h" + +namespace mozilla { +namespace image { + +#if defined(PR_LOGGING) +static PRLogModuleInfo* gWEBPDecoderAccountingLog = + PR_NewLogModule("WEBPDecoderAccounting"); +#else +#define gWEBPDecoderAccountingLog +#endif + +nsWEBPDecoder::nsWEBPDecoder(RasterImage* aImage) + : Decoder(aImage) + , mDecoder(nullptr) + , mLastLine(0) + , mWidth(0) + , mHeight(0) + , haveSize(false) +{ + MOZ_LOG(gWEBPDecoderAccountingLog, LogLevel::Debug, + ("nsWEBPDecoder::nsWEBPDecoder: Creating WEBP decoder %p", + this)); +} + +nsWEBPDecoder::~nsWEBPDecoder() +{ + MOZ_LOG(gWEBPDecoderAccountingLog, LogLevel::Debug, + ("nsWEBPDecoder::~nsWEBPDecoder: Destroying WEBP decoder %p", + this)); +} + +void +nsWEBPDecoder::InitInternal() +{ + if (!WebPInitDecBuffer(&mDecBuf)) { + PostDecoderError(NS_ERROR_FAILURE); + return; + } + + if (IsMetadataDecode()) { + return; // don't need a decoder yet + } + + MOZ_ASSERT(!mImageData, "Shouldn't have a buffer yet"); + + mDecBuf.colorspace = MODE_bgrA; + mDecBuf.is_external_memory = 1; + mDecBuf.u.RGBA.rgba = mImageData; // nullptr right now + mDecoder = WebPINewDecoder(&mDecBuf); + if (!mDecoder) { + PostDecoderError(NS_ERROR_FAILURE); + return; + } +} + +void +nsWEBPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount) +{ + const uint8_t* buf = (const uint8_t*)aBuffer; + + if (IsMetadataDecode() || !haveSize) { + WebPBitstreamFeatures features; + const VP8StatusCode rv = WebPGetFeatures(buf, aCount, &features); + + if (rv == VP8_STATUS_OK) { + if (features.has_animation) { + PostDecoderError(NS_ERROR_FAILURE); + return; + } + if (features.has_alpha) { + PostHasTransparency(); + } + // Post our size to the superclass + if (IsMetadataDecode()) { + PostSize(features.width, features.height); + } + mWidth = features.width; + mHeight = features.height; + mDecBuf.width = mWidth; + mDecBuf.height = mHeight; + mDecBuf.u.RGBA.stride = mWidth * sizeof(uint32_t); + mDecBuf.u.RGBA.size = mDecBuf.u.RGBA.stride * mHeight; + haveSize = true; + } else if (rv != VP8_STATUS_NOT_ENOUGH_DATA) { + PostDecoderError(NS_ERROR_FAILURE); + return; + } else { +#if DEBUG + fprintf(stderr, "WebP had unexpected return code: %d\n", rv); +#endif + return; + } + + // If we're doing a size decode, we're done. + if (IsMetadataDecode()) return; + } + + MOZ_ASSERT(!mImageData, "Already have a buffer allocated?"); + MOZ_ASSERT(haveSize, "Didn't fetch metadata?"); + PostSize(mWidth, mHeight); + nsresult rv_ = AllocateBasicFrame(); + if (NS_FAILED(rv_)) { + return; + } + + MOZ_ASSERT(mImageData, "Should have a buffer now"); + MOZ_ASSERT(mDecoder, "Should have a decoder now"); + mDecBuf.u.RGBA.rgba = mImageData; // no longer null + const VP8StatusCode rv = WebPIAppend(mDecoder, buf, aCount); + + if (rv == VP8_STATUS_OUT_OF_MEMORY) { + PostDecoderError(NS_ERROR_OUT_OF_MEMORY); + return; + } else if (rv == VP8_STATUS_INVALID_PARAM || + rv == VP8_STATUS_BITSTREAM_ERROR) { + PostDataError(); + return; + } else if (rv == VP8_STATUS_UNSUPPORTED_FEATURE || + rv == VP8_STATUS_USER_ABORT) { + PostDecoderError(NS_ERROR_FAILURE); + return; + } + + // Catch any remaining erroneous return value. + if (rv != VP8_STATUS_OK && rv != VP8_STATUS_SUSPENDED) { + PostDecoderError(NS_ERROR_FAILURE); + return; + } + + int lastLineRead = -1; + int width = 0; + int height = 0; + int stride = 0; + + const uint8_t* data = + WebPIDecGetRGB(mDecoder, &lastLineRead, &width, &height, &stride); + + // WebP encoded image data hasn't been read yet, return. + if (lastLineRead == -1 || !data) { + return; + } + + // Ensure valid image height & width. + if (width <= 0 || height <= 0) { + PostDataError(); + return; + } + + if (!mImageData) { + PostDecoderError(NS_ERROR_FAILURE); + return; + } + + if (lastLineRead > mLastLine) { + // Invalidate + nsIntRect r(0, mLastLine, width, lastLineRead - mLastLine); + PostInvalidation(r); + mLastLine = lastLineRead; + } +} + +void +nsWEBPDecoder::FinishInternal() +{ + // We should never make multiple frames + MOZ_ASSERT(GetFrameCount() <= 1, "Multiple WebP frames?"); + + // Send notifications if appropriate + if (!IsMetadataDecode() && (GetFrameCount() == 1)) { + // Flush the decoder + WebPIDelete(mDecoder); + WebPFreeDecBuffer(&mDecBuf); + + PostFrameStop(); + PostDecodeDone(); + + mDecoder = nullptr; + } +} + +} // namespace image +} // namespace mozilla diff --git a/image/decoders/nsWEBPDecoder.h b/image/decoders/nsWEBPDecoder.h new file mode 100644 index 000000000..c1ffe6ebe --- /dev/null +++ b/image/decoders/nsWEBPDecoder.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vikas Arora + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsWEBPDecoder_h +#define nsWEBPDecoder_h + +#include "Decoder.h" + +extern "C" { +#include "webp/decode.h" +} + +namespace mozilla { +namespace image { +class RasterImage; + +////////////////////////////////////////////////////////////////////// +// nsWEBPDecoder Definition + +class nsWEBPDecoder : public Decoder +{ +public: + explicit nsWEBPDecoder(RasterImage* aImage); + virtual ~nsWEBPDecoder(); + + virtual void InitInternal() override; + virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override; + virtual void FinishInternal() override; + +private: + WebPIDecoder* mDecoder; // Pointer to Incremental WebP Decoder. + WebPDecBuffer mDecBuf; // Decoder buffer for output RGBA data. + int mLastLine; // Last image scan-line read so far. + int mWidth; // Image Width + int mHeight; // Image Height + bool haveSize; // True if mDecBuf contains image dimension +}; + +} // namespace image +} // namespace mozilla + +#endif // nsWEBPDecoder_h