mirror of
https://github.com/classilla/tenfourfox.git
synced 2025-02-22 02:29:14 +00:00
#501: convert to Demux API; add colour profile support
This commit is contained in:
parent
c2ce1fbd04
commit
ceed596553
@ -3,14 +3,17 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
/* 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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
/* Contributor(s):
|
|
||||||
* Vikas Arora <vikasa@google.com> */
|
/* WebP for TenFourFox. Actually works on big endian.
|
||||||
|
Incorporates original version from bug 600919 with later updates.
|
||||||
|
See also bug 1294490. */
|
||||||
|
|
||||||
#include "nsWEBPDecoder.h"
|
#include "nsWEBPDecoder.h"
|
||||||
|
|
||||||
#include "ImageLogging.h"
|
#include "ImageLogging.h"
|
||||||
#include "gfxColor.h"
|
#include "gfxColor.h"
|
||||||
#include "gfxPlatform.h"
|
#include "gfxPlatform.h"
|
||||||
|
#include "webp/demux.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace image {
|
namespace image {
|
||||||
@ -29,6 +32,8 @@ nsWEBPDecoder::nsWEBPDecoder(RasterImage* aImage)
|
|||||||
, mWidth(0)
|
, mWidth(0)
|
||||||
, mHeight(0)
|
, mHeight(0)
|
||||||
, haveSize(false)
|
, haveSize(false)
|
||||||
|
, mProfile(nullptr)
|
||||||
|
, mTransform(nullptr)
|
||||||
{
|
{
|
||||||
MOZ_LOG(gWEBPDecoderAccountingLog, LogLevel::Debug,
|
MOZ_LOG(gWEBPDecoderAccountingLog, LogLevel::Debug,
|
||||||
("nsWEBPDecoder::nsWEBPDecoder: Creating WEBP decoder %p",
|
("nsWEBPDecoder::nsWEBPDecoder: Creating WEBP decoder %p",
|
||||||
@ -56,7 +61,9 @@ nsWEBPDecoder::InitInternal()
|
|||||||
|
|
||||||
MOZ_ASSERT(!mImageData, "Shouldn't have a buffer yet");
|
MOZ_ASSERT(!mImageData, "Shouldn't have a buffer yet");
|
||||||
|
|
||||||
#ifdef __ppc__
|
// Premultiplied alpha required. We may change the colourspace
|
||||||
|
// if colour management is required (see below).
|
||||||
|
#if MOZ_BIG_ENDIAN
|
||||||
mDecBuf.colorspace = MODE_Argb;
|
mDecBuf.colorspace = MODE_Argb;
|
||||||
#else
|
#else
|
||||||
mDecBuf.colorspace = MODE_bgrA;
|
mDecBuf.colorspace = MODE_bgrA;
|
||||||
@ -74,72 +81,134 @@ void
|
|||||||
nsWEBPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
nsWEBPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
||||||
{
|
{
|
||||||
const uint8_t* buf = (const uint8_t*)aBuffer;
|
const uint8_t* buf = (const uint8_t*)aBuffer;
|
||||||
|
uint32_t flags = 0;
|
||||||
|
|
||||||
if (IsMetadataDecode() || !haveSize) {
|
if (!haveSize) {
|
||||||
WebPBitstreamFeatures features;
|
WebPDemuxer *demux = nullptr;
|
||||||
const VP8StatusCode rv = WebPGetFeatures(buf, aCount, &features);
|
WebPDemuxState state;
|
||||||
|
WebPData fragment;
|
||||||
|
|
||||||
if (rv == VP8_STATUS_OK) {
|
if (!aCount) return;
|
||||||
if (features.has_animation) {
|
|
||||||
PostDecoderError(NS_ERROR_FAILURE);
|
fragment.bytes = buf;
|
||||||
return;
|
fragment.size = aCount;
|
||||||
}
|
demux = WebPDemuxPartial(&fragment, &state);
|
||||||
if (features.has_alpha) {
|
if (!demux) {
|
||||||
PostHasTransparency();
|
// We don't even have enough data to determine if we have headers.
|
||||||
}
|
|
||||||
// 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;
|
return;
|
||||||
} else {
|
}
|
||||||
#if DEBUG
|
if (state == WEBP_DEMUX_PARSE_ERROR) {
|
||||||
fprintf(stderr, "WebP had unexpected return code: %d\n", rv);
|
// Hmm.
|
||||||
#endif
|
NS_WARNING("Parse error on WebP image");
|
||||||
|
WebPDemuxDelete(demux);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (state == WEBP_DEMUX_PARSING_HEADER) {
|
||||||
|
// Not enough data yet.
|
||||||
|
WebPDemuxDelete(demux);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're doing a size decode, we're done.
|
flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
|
||||||
if (IsMetadataDecode()) return;
|
|
||||||
|
// Make sure chunks are available, if we need them.
|
||||||
|
if (flags & ICCP_FLAG) {
|
||||||
|
// Embedded colour profile chunk.
|
||||||
|
if (state != WEBP_DEMUX_DONE) {
|
||||||
|
// Not enough data yet.
|
||||||
|
NS_WARNING("Waiting for WebP chunks to load");
|
||||||
|
WebPDemuxDelete(demux);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid demuxer available.
|
||||||
|
if (flags & ANIMATION_FLAG) {
|
||||||
|
NS_WARNING("animated WebP not yet supported"); // XXX
|
||||||
|
}
|
||||||
|
if (flags & ALPHA_FLAG) {
|
||||||
|
PostHasTransparency();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle embedded colour profiles.
|
||||||
|
if (!mProfile && (flags & ICCP_FLAG)) {
|
||||||
|
if (gfxPlatform::GetCMSOutputProfile()) {
|
||||||
|
WebPChunkIterator chunk_iter;
|
||||||
|
|
||||||
|
if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) {
|
||||||
|
#if DEBUG
|
||||||
|
fprintf(stderr, "WebP has embedded colour profile (%d bytes).\n", chunk_iter.chunk.size);
|
||||||
|
#endif
|
||||||
|
mProfile = qcms_profile_from_memory(
|
||||||
|
reinterpret_cast<const char*>(chunk_iter.chunk.bytes),
|
||||||
|
chunk_iter.chunk.size);
|
||||||
|
if (mProfile) {
|
||||||
|
int intent = gfxPlatform::GetRenderingIntent();
|
||||||
|
if (intent == -1)
|
||||||
|
intent = qcms_profile_get_rendering_intent(mProfile);
|
||||||
|
mTransform = qcms_transform_create(mProfile,
|
||||||
|
QCMS_DATA_RGBA_8,
|
||||||
|
gfxPlatform::GetCMSOutputProfile(),
|
||||||
|
QCMS_DATA_RGBA_8,
|
||||||
|
(qcms_intent)intent);
|
||||||
|
mDecBuf.colorspace = MODE_rgbA; // byte-swapped later
|
||||||
|
}
|
||||||
|
WebPDemuxReleaseChunkIterator(&chunk_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mWidth = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
|
||||||
|
mHeight = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
|
||||||
|
// Post our size to the superclass
|
||||||
|
PostSize(mWidth, mHeight);
|
||||||
|
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;
|
||||||
|
|
||||||
|
WebPDemuxDelete(demux);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
|
if (IsMetadataDecode()) {
|
||||||
MOZ_ASSERT(haveSize, "Didn't fetch metadata?");
|
// Nothing else to do.
|
||||||
PostSize(mWidth, mHeight);
|
return;
|
||||||
nsresult rv_ = AllocateBasicFrame();
|
}
|
||||||
if (NS_FAILED(rv_)) {
|
|
||||||
|
if (!mImageData) {
|
||||||
|
MOZ_ASSERT(haveSize, "Didn't fetch metadata?");
|
||||||
|
nsresult rv_ = AllocateBasicFrame();
|
||||||
|
if (NS_FAILED(rv_)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
MOZ_ASSERT(mImageData, "Should have a buffer now");
|
||||||
MOZ_ASSERT(mDecoder, "Should have a decoder now");
|
MOZ_ASSERT(mDecoder, "Should have a decoder now");
|
||||||
mDecBuf.u.RGBA.rgba = mImageData; // no longer null
|
mDecBuf.u.RGBA.rgba = mImageData; // no longer null
|
||||||
const VP8StatusCode rv = WebPIAppend(mDecoder, buf, aCount);
|
const VP8StatusCode rv = WebPIAppend(mDecoder, buf, aCount);
|
||||||
|
|
||||||
if (rv == VP8_STATUS_OUT_OF_MEMORY) {
|
if (rv == VP8_STATUS_OUT_OF_MEMORY) {
|
||||||
|
NS_WARNING("WebP out of memory");
|
||||||
PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
|
PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return;
|
return;
|
||||||
} else if (rv == VP8_STATUS_INVALID_PARAM ||
|
} else if (rv == VP8_STATUS_INVALID_PARAM ||
|
||||||
rv == VP8_STATUS_BITSTREAM_ERROR) {
|
rv == VP8_STATUS_BITSTREAM_ERROR) {
|
||||||
|
NS_WARNING("WebP bitstream error");
|
||||||
PostDataError();
|
PostDataError();
|
||||||
return;
|
return;
|
||||||
} else if (rv == VP8_STATUS_UNSUPPORTED_FEATURE ||
|
} else if (rv == VP8_STATUS_UNSUPPORTED_FEATURE ||
|
||||||
rv == VP8_STATUS_USER_ABORT) {
|
rv == VP8_STATUS_USER_ABORT) {
|
||||||
|
NS_WARNING("WebP unsupported feature");
|
||||||
PostDecoderError(NS_ERROR_FAILURE);
|
PostDecoderError(NS_ERROR_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch any remaining erroneous return value.
|
// Catch any remaining erroneous return value.
|
||||||
if (rv != VP8_STATUS_OK && rv != VP8_STATUS_SUSPENDED) {
|
if (rv != VP8_STATUS_OK && rv != VP8_STATUS_SUSPENDED) {
|
||||||
|
#if DEBUG
|
||||||
|
fprintf(stderr, "WebP unexpected parsing error %d\n", rv);
|
||||||
|
#endif
|
||||||
PostDecoderError(NS_ERROR_FAILURE);
|
PostDecoderError(NS_ERROR_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -149,7 +218,7 @@ nsWEBPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
int height = 0;
|
int height = 0;
|
||||||
int stride = 0;
|
int stride = 0;
|
||||||
|
|
||||||
const uint8_t* data =
|
uint8_t* data =
|
||||||
WebPIDecGetRGB(mDecoder, &lastLineRead, &width, &height, &stride);
|
WebPIDecGetRGB(mDecoder, &lastLineRead, &width, &height, &stride);
|
||||||
|
|
||||||
// WebP encoded image data hasn't been read yet, return.
|
// WebP encoded image data hasn't been read yet, return.
|
||||||
@ -164,11 +233,35 @@ nsWEBPDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!mImageData) {
|
if (!mImageData) {
|
||||||
|
NS_WARNING("WebP y u no haz image");
|
||||||
PostDecoderError(NS_ERROR_FAILURE);
|
PostDecoderError(NS_ERROR_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastLineRead > mLastLine) {
|
if (lastLineRead > mLastLine) {
|
||||||
|
if (mTransform) {
|
||||||
|
for (int row = mLastLine; row < lastLineRead; row++) {
|
||||||
|
MOZ_ASSERT(!(stride % 4)); // should be RGBA alignment
|
||||||
|
uint8_t* src = data + row * stride;
|
||||||
|
qcms_transform_data(mTransform, src, src, width);
|
||||||
|
for (uint8_t* i = src; i < src + stride; i+=4) {
|
||||||
|
#if MOZ_BIG_ENDIAN
|
||||||
|
// RGBA -> ARGB
|
||||||
|
uint8_t a = i[3];
|
||||||
|
i[3] = i[2];
|
||||||
|
i[2] = i[1];
|
||||||
|
i[1] = i[0];
|
||||||
|
i[0] = a;
|
||||||
|
#else
|
||||||
|
// RGBA -> BGRA
|
||||||
|
uint8_t r = i[2];
|
||||||
|
i[2] = i[0];
|
||||||
|
i[0] = r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Invalidate
|
// Invalidate
|
||||||
nsIntRect r(0, mLastLine, width, lastLineRead - mLastLine);
|
nsIntRect r(0, mLastLine, width, lastLineRead - mLastLine);
|
||||||
PostInvalidation(r);
|
PostInvalidation(r);
|
||||||
@ -192,6 +285,15 @@ nsWEBPDecoder::FinishInternal()
|
|||||||
PostDecodeDone();
|
PostDecodeDone();
|
||||||
|
|
||||||
mDecoder = nullptr;
|
mDecoder = nullptr;
|
||||||
|
|
||||||
|
if (mProfile) {
|
||||||
|
if (mTransform) {
|
||||||
|
qcms_transform_release(mTransform);
|
||||||
|
mTransform = nullptr;
|
||||||
|
}
|
||||||
|
qcms_profile_release(mProfile);
|
||||||
|
mProfile = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +70,9 @@ private:
|
|||||||
int mWidth; // Image Width
|
int mWidth; // Image Width
|
||||||
int mHeight; // Image Height
|
int mHeight; // Image Height
|
||||||
bool haveSize; // True if mDecBuf contains image dimension
|
bool haveSize; // True if mDecBuf contains image dimension
|
||||||
|
|
||||||
|
qcms_profile* mProfile; // embedded ICC profile
|
||||||
|
qcms_transform* mTransform; // resulting qcms transform
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace image
|
} // namespace image
|
||||||
|
@ -4,13 +4,13 @@
|
|||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# 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/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
|
||||||
src_dir = 'src/'
|
src_dir = 'src/'
|
||||||
moz_dir = src_dir + 'moz/'
|
moz_dir = src_dir + 'moz/'
|
||||||
dec_dir = src_dir + 'dec/'
|
dec_dir = src_dir + 'dec/'
|
||||||
dsp_dir = src_dir + 'dsp/'
|
dsp_dir = src_dir + 'dsp/'
|
||||||
utils_dir = src_dir + 'utils/'
|
utils_dir = src_dir + 'utils/'
|
||||||
webp_dir = src_dir + 'webp/'
|
webp_dir = src_dir + 'webp/'
|
||||||
|
demux_dir = src_dir + 'demux/'
|
||||||
|
|
||||||
EXPORTS.webp += [
|
EXPORTS.webp += [
|
||||||
webp_dir + 'decode.h',
|
webp_dir + 'decode.h',
|
||||||
@ -32,6 +32,10 @@ dec_sources = [
|
|||||||
dec_dir + 'webp_dec.c',
|
dec_dir + 'webp_dec.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
demux_sources = [
|
||||||
|
demux_dir + 'demux.c',
|
||||||
|
]
|
||||||
|
|
||||||
dsp_sources = [
|
dsp_sources = [
|
||||||
dsp_dir + 'alpha_processing.c',
|
dsp_dir + 'alpha_processing.c',
|
||||||
dsp_dir + 'alpha_processing_sse2.c',
|
dsp_dir + 'alpha_processing_sse2.c',
|
||||||
@ -68,31 +72,10 @@ utils_sources = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# NB: dsp especially isn't UNIFIED_SOURCES friendly.
|
# NB: dsp especially isn't UNIFIED_SOURCES friendly.
|
||||||
SOURCES += dec_sources + dsp_sources + moz_sources + utils_sources
|
SOURCES += dec_sources + demux_sources + dsp_sources + moz_sources + utils_sources
|
||||||
|
|
||||||
if CONFIG['OS_TARGET'] == 'Android':
|
|
||||||
# Older versions of the Android NDK don't pre-define anything to indicate
|
|
||||||
# the OS they're on, so do it for them.
|
|
||||||
DEFINES['__linux__'] = True
|
|
||||||
CFLAGS += [
|
|
||||||
'-I%s/sources/android/cpufeatures/' % CONFIG['ANDROID_NDK'],
|
|
||||||
]
|
|
||||||
|
|
||||||
if not CONFIG['MOZ_WEBRTC']:
|
|
||||||
SOURCES += [
|
|
||||||
'%s/sources/android/cpufeatures/cpu-features.c' % CONFIG['ANDROID_NDK'],
|
|
||||||
]
|
|
||||||
|
|
||||||
# TODO: aarch64?
|
|
||||||
if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC']:
|
|
||||||
for f in dsp_sources:
|
|
||||||
if f.endswith('.c') and 'neon' in f:
|
|
||||||
SOURCES[f].flags += ['-mfloat-abi=softfp', '-mfpu=neon']
|
|
||||||
|
|
||||||
Library('mozwebp')
|
Library('mozwebp')
|
||||||
|
|
||||||
#MSVC_ENABLE_PGO = True
|
|
||||||
|
|
||||||
if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
|
if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
|
||||||
NO_VISIBILITY_FLAGS = True
|
NO_VISIBILITY_FLAGS = True
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user