зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1680387 - Read and expose EXIF image resolution data. r=tnikkel,aosmond
Differential Revision: https://phabricator.services.mozilla.com/D113264
This commit is contained in:
Родитель
dc8fb2b8d5
Коммит
6c4266f7f7
|
@ -449,13 +449,13 @@ nsresult Decoder::FinishWithErrorInternal() {
|
|||
*/
|
||||
|
||||
void Decoder::PostSize(int32_t aWidth, int32_t aHeight,
|
||||
Orientation aOrientation /* = Orientation()*/) {
|
||||
Orientation aOrientation, Resolution aResolution) {
|
||||
// Validate.
|
||||
MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
|
||||
MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
|
||||
|
||||
// Set our intrinsic size.
|
||||
mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
|
||||
mImageMetadata.SetSize(aWidth, aHeight, aOrientation, aResolution);
|
||||
|
||||
// Verify it is the expected size, if given. Note that this is only used by
|
||||
// the ICO decoder for embedded image types, so only its subdecoders are
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "DecoderFlags.h"
|
||||
#include "ImageMetadata.h"
|
||||
#include "Orientation.h"
|
||||
#include "Resolution.h"
|
||||
#include "SourceBuffer.h"
|
||||
#include "StreamingLexer.h"
|
||||
#include "SurfaceFlags.h"
|
||||
|
@ -473,8 +474,8 @@ class Decoder {
|
|||
|
||||
// Called by decoders when they determine the size of the image. Informs
|
||||
// the image of its size and sends notifications.
|
||||
void PostSize(int32_t aWidth, int32_t aHeight,
|
||||
Orientation aOrientation = Orientation());
|
||||
void PostSize(int32_t aWidth, int32_t aHeight, Orientation = Orientation(),
|
||||
Resolution = Resolution());
|
||||
|
||||
// Called by decoders if they determine that the image has transparency.
|
||||
//
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/SVGImageContext.h"
|
||||
#include "ImageRegion.h"
|
||||
#include "Orientation.h"
|
||||
#include "mozilla/image/Resolution.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
|
@ -113,6 +114,9 @@ Maybe<AspectRatio> DynamicImage::GetIntrinsicRatio() {
|
|||
NS_IMETHODIMP_(Orientation)
|
||||
DynamicImage::GetOrientation() { return Orientation(); }
|
||||
|
||||
NS_IMETHODIMP_(Resolution)
|
||||
DynamicImage::GetResolution() { return {}; }
|
||||
|
||||
NS_IMETHODIMP
|
||||
DynamicImage::GetType(uint16_t* aType) {
|
||||
*aType = imgIContainer::TYPE_RASTER;
|
||||
|
|
|
@ -14,19 +14,16 @@
|
|||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/image/Resolution.h"
|
||||
#include "nsSize.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace mozilla::image {
|
||||
|
||||
// The metadata about an image that decoders accumulate as they decode.
|
||||
class ImageMetadata {
|
||||
public:
|
||||
ImageMetadata()
|
||||
: mLoopCount(-1),
|
||||
mFirstFrameTimeout(FrameTimeout::Forever()),
|
||||
mHasAnimation(false) {}
|
||||
ImageMetadata() = default;
|
||||
|
||||
void SetHotspot(uint16_t aHotspotX, uint16_t aHotspotY) {
|
||||
mHotspot = Some(gfx::IntPoint(aHotspotX, aHotspotY));
|
||||
|
@ -56,10 +53,12 @@ class ImageMetadata {
|
|||
return mFirstFrameRefreshArea.isSome();
|
||||
}
|
||||
|
||||
void SetSize(int32_t width, int32_t height, Orientation orientation) {
|
||||
void SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation,
|
||||
Resolution aResolution) {
|
||||
if (!HasSize()) {
|
||||
mSize.emplace(nsIntSize(width, height));
|
||||
mOrientation.emplace(orientation);
|
||||
mSize.emplace(nsIntSize(aWidth, aHeight));
|
||||
mOrientation.emplace(aOrientation);
|
||||
mResolution = aResolution;
|
||||
}
|
||||
}
|
||||
nsIntSize GetSize() const { return *mSize; }
|
||||
|
@ -69,6 +68,8 @@ class ImageMetadata {
|
|||
mNativeSizes.AppendElement(aSize);
|
||||
}
|
||||
|
||||
Resolution GetResolution() const { return mResolution; }
|
||||
|
||||
const nsTArray<nsIntSize>& GetNativeSizes() const { return mNativeSizes; }
|
||||
|
||||
Orientation GetOrientation() const { return *mOrientation; }
|
||||
|
@ -82,13 +83,16 @@ class ImageMetadata {
|
|||
Maybe<gfx::IntPoint> mHotspot;
|
||||
|
||||
/// The loop count for animated images, or -1 for infinite loop.
|
||||
int32_t mLoopCount;
|
||||
int32_t mLoopCount = -1;
|
||||
|
||||
/// The resolution of the image in dppx.
|
||||
Resolution mResolution;
|
||||
|
||||
// The total length of a single loop through an animated image.
|
||||
Maybe<FrameTimeout> mLoopLength;
|
||||
|
||||
/// The timeout of an animated image's first frame.
|
||||
FrameTimeout mFirstFrameTimeout;
|
||||
FrameTimeout mFirstFrameTimeout = FrameTimeout::Forever();
|
||||
|
||||
// The area of the image that needs to be invalidated when the animation
|
||||
// loops.
|
||||
|
@ -100,10 +104,9 @@ class ImageMetadata {
|
|||
// Sizes the image can natively decode to.
|
||||
CopyableTArray<nsIntSize> mNativeSizes;
|
||||
|
||||
bool mHasAnimation : 1;
|
||||
bool mHasAnimation = false;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::image
|
||||
|
||||
#endif // mozilla_image_ImageMetadata_h
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "Orientation.h"
|
||||
#include "mozilla/image/Resolution.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using dom::Document;
|
||||
using gfx::DataSourceSurface;
|
||||
using gfx::IntSize;
|
||||
using gfx::SamplingFilter;
|
||||
using gfx::SourceSurface;
|
||||
|
@ -135,6 +135,9 @@ nsresult ImageWrapper::GetHotspotY(int32_t* aY) {
|
|||
NS_IMETHODIMP_(Orientation)
|
||||
ImageWrapper::GetOrientation() { return mInnerImage->GetOrientation(); }
|
||||
|
||||
NS_IMETHODIMP_(Resolution)
|
||||
ImageWrapper::GetResolution() { return mInnerImage->GetResolution(); }
|
||||
|
||||
NS_IMETHODIMP
|
||||
ImageWrapper::GetType(uint16_t* aType) { return mInnerImage->GetType(aType); }
|
||||
|
||||
|
|
|
@ -259,6 +259,9 @@ Maybe<AspectRatio> RasterImage::GetIntrinsicRatio() {
|
|||
NS_IMETHODIMP_(Orientation)
|
||||
RasterImage::GetOrientation() { return mOrientation; }
|
||||
|
||||
NS_IMETHODIMP_(Resolution)
|
||||
RasterImage::GetResolution() { return mResolution; }
|
||||
|
||||
//******************************************************************************
|
||||
NS_IMETHODIMP
|
||||
RasterImage::GetType(uint16_t* aType) {
|
||||
|
@ -710,6 +713,8 @@ bool RasterImage::SetMetadata(const ImageMetadata& aMetadata,
|
|||
return true;
|
||||
}
|
||||
|
||||
mResolution = aMetadata.GetResolution();
|
||||
|
||||
if (aMetadata.HasSize()) {
|
||||
auto metadataSize = UnorientedIntSize::FromUnknownSize(aMetadata.GetSize());
|
||||
if (metadataSize.width < 0 || metadataSize.height < 0) {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/image/Resolution.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "PlaybackType.h"
|
||||
#ifdef DEBUG
|
||||
|
@ -412,6 +413,9 @@ class RasterImage final : public ImageResource,
|
|||
// metadata. RasterImage will handle and apply this orientation itself.
|
||||
Orientation mOrientation;
|
||||
|
||||
// The resolution as specified in the image metadata, in dppx.
|
||||
Resolution mResolution;
|
||||
|
||||
/// If this has a value, we're waiting for SetSize() to send the load event.
|
||||
Maybe<Progress> mLoadProgress;
|
||||
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_image_Resolution_h
|
||||
#define mozilla_image_Resolution_h
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
/**
|
||||
* The resolution of an image, in dppx.
|
||||
*/
|
||||
struct Resolution {
|
||||
Resolution() = default;
|
||||
Resolution(float aX, float aY) : mX(aX), mY(aY) {
|
||||
MOZ_ASSERT(mX != 0.0f);
|
||||
MOZ_ASSERT(mY != 0.0f);
|
||||
}
|
||||
|
||||
bool operator==(const Resolution& aOther) const {
|
||||
return mX == aOther.mX && mY == aOther.mY;
|
||||
}
|
||||
bool operator!=(const Resolution& aOther) const { return !(*this == aOther); }
|
||||
|
||||
float mX = 1.0f;
|
||||
float mY = 1.0f;
|
||||
|
||||
void ApplyXTo(int32_t& aWidth) const {
|
||||
if (mX != 1.0f) {
|
||||
aWidth = std::round(float(aWidth) / mX);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyXTo(float& aWidth) const {
|
||||
if (mX != 1.0f) {
|
||||
aWidth /= mX;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyYTo(int32_t& aHeight) const {
|
||||
if (mY != 1.0f) {
|
||||
aHeight = std::round(float(aHeight) / mY);
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyYTo(float& aHeight) const {
|
||||
if (mY != 1.0f) {
|
||||
aHeight /= mY;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyTo(int32_t& aWidth, int32_t& aHeight) const {
|
||||
ApplyXTo(aWidth);
|
||||
ApplyYTo(aHeight);
|
||||
}
|
||||
|
||||
void ApplyTo(float& aWidth, float& aHeight) const {
|
||||
ApplyXTo(aWidth);
|
||||
ApplyYTo(aHeight);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
||||
using ImageResolution = image::Resolution;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -44,6 +44,7 @@
|
|||
#include "SurfaceCache.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "mozilla/image/Resolution.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -603,6 +604,9 @@ Maybe<AspectRatio> VectorImage::GetIntrinsicRatio() {
|
|||
NS_IMETHODIMP_(Orientation)
|
||||
VectorImage::GetOrientation() { return Orientation(); }
|
||||
|
||||
NS_IMETHODIMP_(Resolution)
|
||||
VectorImage::GetResolution() { return {}; }
|
||||
|
||||
//******************************************************************************
|
||||
NS_IMETHODIMP
|
||||
VectorImage::GetType(uint16_t* aType) {
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
#include "EXIF.h"
|
||||
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/StaticPrefs_image.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace mozilla::image {
|
||||
|
||||
// Section references in this file refer to the EXIF v2.3 standard, also known
|
||||
// as CIPA DC-008-Translation-2010.
|
||||
|
@ -16,8 +16,11 @@ namespace image {
|
|||
// See Section 4.6.4, Table 4.
|
||||
// Typesafe enums are intentionally not used here since we're comparing to raw
|
||||
// integers produced by parsing.
|
||||
enum EXIFTag {
|
||||
OrientationTag = 0x112,
|
||||
enum class EXIFTag : uint16_t {
|
||||
Orientation = 0x112,
|
||||
XResolution = 0x11a,
|
||||
YResolution = 0x11b,
|
||||
ResolutionUnit = 0x128,
|
||||
};
|
||||
|
||||
// See Section 4.6.2.
|
||||
|
@ -34,6 +37,26 @@ enum EXIFType {
|
|||
|
||||
static const char* EXIFHeader = "Exif\0\0";
|
||||
static const uint32_t EXIFHeaderLength = 6;
|
||||
static const uint32_t TIFFHeaderStart = EXIFHeaderLength;
|
||||
|
||||
struct ParsedEXIFData {
|
||||
Orientation orientation;
|
||||
float resolutionX = 72.0f;
|
||||
float resolutionY = 72.0f;
|
||||
ResolutionUnit resolutionUnit = ResolutionUnit::Dpi;
|
||||
};
|
||||
|
||||
static float ToDppx(float aResolution, ResolutionUnit aUnit) {
|
||||
constexpr float kPointsPerInch = 72.0f;
|
||||
constexpr float kPointsPerCm = 1.0f / 2.54f;
|
||||
switch (aUnit) {
|
||||
case ResolutionUnit::Dpi:
|
||||
return aResolution / kPointsPerInch;
|
||||
case ResolutionUnit::Dpcm:
|
||||
return aResolution / kPointsPerCm;
|
||||
}
|
||||
MOZ_CRASH("Unknown resolution unit?");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// Parse EXIF data, typically found in a JPEG's APP1 segment.
|
||||
|
@ -54,14 +77,13 @@ EXIFData EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength) {
|
|||
|
||||
JumpTo(offsetIFD);
|
||||
|
||||
Orientation orientation;
|
||||
if (!ParseIFD0(orientation)) {
|
||||
return EXIFData();
|
||||
}
|
||||
|
||||
// We only care about orientation at this point, so we don't bother with the
|
||||
// other IFDs. If we got this far we're done.
|
||||
return EXIFData(orientation);
|
||||
// We only care about IFD0 at this point, so we don't bother with the other
|
||||
// IFDs. If we got this far we're done.
|
||||
ParsedEXIFData data;
|
||||
ParseIFD0(data);
|
||||
return EXIFData{data.orientation,
|
||||
Resolution(ToDppx(data.resolutionX, data.resolutionUnit),
|
||||
ToDppx(data.resolutionY, data.resolutionUnit))};
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
|
@ -94,55 +116,134 @@ bool EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut) {
|
|||
// The IFD offset is relative to the beginning of the TIFF header, which
|
||||
// begins after the EXIF header, so we need to increase the offset
|
||||
// appropriately.
|
||||
aIFD0OffsetOut = ifd0Offset + EXIFHeaderLength;
|
||||
aIFD0OffsetOut = ifd0Offset + TIFFHeaderStart;
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Parse the entries in IFD0. (Section 4.6.2)
|
||||
/////////////////////////////////////////////////////////
|
||||
bool EXIFParser::ParseIFD0(Orientation& aOrientationOut) {
|
||||
void EXIFParser::ParseIFD0(ParsedEXIFData& aData) {
|
||||
uint16_t entryCount;
|
||||
if (!ReadUInt16(entryCount)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint16_t entry = 0; entry < entryCount; ++entry) {
|
||||
// Read the fields of the entry.
|
||||
// Read the fields of the 12-byte entry.
|
||||
uint16_t tag;
|
||||
if (!ReadUInt16(tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Right now, we only care about orientation, so we immediately skip to the
|
||||
// next entry if we find anything else.
|
||||
if (tag != OrientationTag) {
|
||||
Advance(10);
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t type;
|
||||
if (!ReadUInt16(type)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t count;
|
||||
if (!ReadUInt32(count)) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// We should have an orientation value here; go ahead and parse it.
|
||||
if (!ParseOrientation(type, count, aOrientationOut)) {
|
||||
return false;
|
||||
switch (EXIFTag(tag)) {
|
||||
case EXIFTag::Orientation:
|
||||
// We should have an orientation value here; go ahead and parse it.
|
||||
if (!ParseOrientation(type, count, aData.orientation)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case EXIFTag::ResolutionUnit:
|
||||
if (!ParseResolutionUnit(type, count, aData.resolutionUnit)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case EXIFTag::XResolution:
|
||||
if (!ParseResolution(type, count, aData.resolutionX)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case EXIFTag::YResolution:
|
||||
if (!ParseResolution(type, count, aData.resolutionY)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Advance(4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since the orientation is all we care about, we're done.
|
||||
bool EXIFParser::ReadRational(float& aOut) {
|
||||
// Values larger than 4 bytes (like rationals) are specified as an offset into
|
||||
// the TIFF header.
|
||||
uint32_t valueOffset;
|
||||
if (!ReadUInt32(valueOffset)) {
|
||||
return false;
|
||||
}
|
||||
ScopedJump jumpToHeader(*this, valueOffset + TIFFHeaderStart);
|
||||
uint32_t numerator;
|
||||
if (!ReadUInt32(numerator)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t denominator;
|
||||
if (!ReadUInt32(denominator)) {
|
||||
return false;
|
||||
}
|
||||
if (denominator == 0) {
|
||||
return false;
|
||||
}
|
||||
aOut = float(numerator) / float(denominator);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EXIFParser::ParseResolution(uint16_t aType, uint32_t aCount, float& aOut) {
|
||||
if (!StaticPrefs::image_exif_density_correction_enabled()) {
|
||||
Advance(4);
|
||||
return true;
|
||||
}
|
||||
if (aType != RationalType || aCount != 1) {
|
||||
return false;
|
||||
}
|
||||
float value;
|
||||
if (!ReadRational(value)) {
|
||||
return false;
|
||||
}
|
||||
if (value == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
aOut = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
// We didn't find an orientation field in the IFD. That's OK; we assume the
|
||||
// default orientation in that case.
|
||||
aOrientationOut = Orientation();
|
||||
bool EXIFParser::ParseResolutionUnit(uint16_t aType, uint32_t aCount,
|
||||
ResolutionUnit& aOut) {
|
||||
if (!StaticPrefs::image_exif_density_correction_enabled()) {
|
||||
Advance(4);
|
||||
return true;
|
||||
}
|
||||
if (aType != ShortType || aCount != 1) {
|
||||
return false;
|
||||
}
|
||||
uint16_t value;
|
||||
if (!ReadUInt16(value)) {
|
||||
return false;
|
||||
}
|
||||
switch (value) {
|
||||
case 2:
|
||||
aOut = ResolutionUnit::Dpi;
|
||||
break;
|
||||
case 3:
|
||||
aOut = ResolutionUnit::Dpcm;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is a 32-bit field, but the unit value only occupies the first 16 bits.
|
||||
// We need to advance another 16 bits to consume the entire field.
|
||||
Advance(2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -319,5 +420,4 @@ bool EXIFParser::ReadUInt32(uint32_t& aValue) {
|
|||
return matched;
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::image
|
||||
|
|
|
@ -10,17 +10,22 @@
|
|||
#include "nsDebug.h"
|
||||
|
||||
#include "Orientation.h"
|
||||
#include "mozilla/image/Resolution.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace mozilla::image {
|
||||
|
||||
enum class ByteOrder : uint8_t { Unknown, LittleEndian, BigEndian };
|
||||
|
||||
struct EXIFData {
|
||||
EXIFData() {}
|
||||
explicit EXIFData(Orientation aOrientation) : orientation(aOrientation) {}
|
||||
const Orientation orientation = Orientation();
|
||||
const Resolution resolution = Resolution();
|
||||
};
|
||||
|
||||
const Orientation orientation;
|
||||
struct ParsedEXIFData;
|
||||
|
||||
enum class ResolutionUnit : uint8_t {
|
||||
Dpi,
|
||||
Dpcm,
|
||||
};
|
||||
|
||||
class EXIFParser {
|
||||
|
@ -41,17 +46,36 @@ class EXIFParser {
|
|||
EXIFData ParseEXIF(const uint8_t* aData, const uint32_t aLength);
|
||||
bool ParseEXIFHeader();
|
||||
bool ParseTIFFHeader(uint32_t& aIFD0OffsetOut);
|
||||
bool ParseIFD0(Orientation& aOrientationOut);
|
||||
bool ParseOrientation(uint16_t aType, uint32_t aCount, Orientation& aOut);
|
||||
|
||||
void ParseIFD0(ParsedEXIFData&);
|
||||
bool ParseOrientation(uint16_t aType, uint32_t aCount, Orientation&);
|
||||
bool ParseResolution(uint16_t aType, uint32_t aCount, float&);
|
||||
bool ParseResolutionUnit(uint16_t aType, uint32_t aCount, ResolutionUnit&);
|
||||
|
||||
bool Initialize(const uint8_t* aData, const uint32_t aLength);
|
||||
void Advance(const uint32_t aDistance);
|
||||
void JumpTo(const uint32_t aOffset);
|
||||
|
||||
uint32_t CurrentOffset() const { return mCurrent - mStart; }
|
||||
|
||||
class ScopedJump {
|
||||
EXIFParser& mParser;
|
||||
uint32_t mOldOffset;
|
||||
|
||||
public:
|
||||
ScopedJump(EXIFParser& aParser, uint32_t aOffset)
|
||||
: mParser(aParser), mOldOffset(aParser.CurrentOffset()) {
|
||||
mParser.JumpTo(aOffset);
|
||||
}
|
||||
|
||||
~ScopedJump() { mParser.JumpTo(mOldOffset); }
|
||||
};
|
||||
|
||||
bool MatchString(const char* aString, const uint32_t aLength);
|
||||
bool MatchUInt16(const uint16_t aValue);
|
||||
bool ReadUInt16(uint16_t& aOut);
|
||||
bool ReadUInt32(uint32_t& aOut);
|
||||
bool ReadRational(float& aOut);
|
||||
|
||||
const uint8_t* mStart;
|
||||
const uint8_t* mCurrent;
|
||||
|
@ -60,7 +84,6 @@ class EXIFParser {
|
|||
ByteOrder mByteOrder;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::image
|
||||
|
||||
#endif // mozilla_image_decoders_EXIF_h
|
||||
|
|
|
@ -236,8 +236,9 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::ReadJPEGData(
|
|||
}
|
||||
|
||||
// Post our size to the superclass
|
||||
PostSize(mInfo.image_width, mInfo.image_height,
|
||||
ReadOrientationFromEXIF());
|
||||
EXIFData exif = ReadExifData();
|
||||
PostSize(mInfo.image_width, mInfo.image_height, exif.orientation,
|
||||
exif.resolution);
|
||||
if (HasError()) {
|
||||
// Setting the size led to an error.
|
||||
mState = JPEG_ERROR;
|
||||
|
@ -601,7 +602,7 @@ LexerTransition<nsJPEGDecoder::State> nsJPEGDecoder::FinishedJPEGData() {
|
|||
return Transition::TerminateFailure();
|
||||
}
|
||||
|
||||
Orientation nsJPEGDecoder::ReadOrientationFromEXIF() {
|
||||
EXIFData nsJPEGDecoder::ReadExifData() const {
|
||||
jpeg_saved_marker_ptr marker;
|
||||
|
||||
// Locate the APP1 marker, where EXIF data is stored, in the marker list.
|
||||
|
@ -613,13 +614,11 @@ Orientation nsJPEGDecoder::ReadOrientationFromEXIF() {
|
|||
|
||||
// If we're at the end of the list, there's no EXIF data.
|
||||
if (!marker) {
|
||||
return Orientation();
|
||||
return EXIFData();
|
||||
}
|
||||
|
||||
// Extract the orientation information.
|
||||
EXIFData exif = EXIFParser::Parse(marker->data,
|
||||
static_cast<uint32_t>(marker->data_length));
|
||||
return exif.orientation;
|
||||
return EXIFParser::Parse(marker->data,
|
||||
static_cast<uint32_t>(marker->data_length));
|
||||
}
|
||||
|
||||
void nsJPEGDecoder::NotifyDone() {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "RasterImage.h"
|
||||
#include "SurfacePipe.h"
|
||||
#include "EXIF.h"
|
||||
|
||||
// On Windows systems, RasterImage.h brings in 'windows.h', which defines INT32.
|
||||
// But the jpeg decoder has its own definition of INT32. To avoid build issues,
|
||||
|
@ -23,8 +24,7 @@ extern "C" {
|
|||
|
||||
#include <setjmp.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
namespace mozilla::image {
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_error_mgr pub; // "public" fields for IJG library
|
||||
|
@ -62,7 +62,7 @@ class nsJPEGDecoder : public Decoder {
|
|||
Maybe<Telemetry::HistogramID> SpeedHistogram() const override;
|
||||
|
||||
protected:
|
||||
Orientation ReadOrientationFromEXIF();
|
||||
EXIFData ReadExifData() const;
|
||||
WriteState OutputScanlines();
|
||||
|
||||
private:
|
||||
|
@ -107,7 +107,6 @@ class nsJPEGDecoder : public Decoder {
|
|||
SurfacePipe mPipe;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
} // namespace mozilla::image
|
||||
|
||||
#endif // mozilla_image_decoders_nsJPEGDecoder_h
|
||||
|
|
|
@ -48,6 +48,7 @@ namespace image {
|
|||
|
||||
class ImageRegion;
|
||||
struct Orientation;
|
||||
struct Resolution;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +72,7 @@ native TempRefImageContainer(already_AddRefed<mozilla::layers::ImageContainer>);
|
|||
[ref] native ImageRegion(mozilla::image::ImageRegion);
|
||||
[ptr] native LayerManager(mozilla::layers::LayerManager);
|
||||
native Orientation(mozilla::image::Orientation);
|
||||
native ImageResolution(mozilla::image::Resolution);
|
||||
[ref] native TimeStamp(mozilla::TimeStamp);
|
||||
[ref] native MaybeSVGImageContext(mozilla::Maybe<mozilla::SVGImageContext>);
|
||||
native TempRefSourceSurface(already_AddRefed<mozilla::gfx::SourceSurface>);
|
||||
|
@ -639,6 +641,12 @@ interface imgIContainer : nsISupports
|
|||
*/
|
||||
[notxpcom] Orientation getOrientation();
|
||||
|
||||
/*
|
||||
* Returns the intrinsic resolution of the image, or 1.0 if the image doesn't
|
||||
* declare any.
|
||||
*/
|
||||
[notxpcom] ImageResolution getResolution();
|
||||
|
||||
/*
|
||||
* Returns the delay, in ms, between the first and second frame. If this
|
||||
* returns 0, there is no delay between first and second frame (i.e., this
|
||||
|
|
|
@ -61,6 +61,7 @@ EXPORTS.mozilla.image += [
|
|||
"encoders/png/nsPNGEncoder.h",
|
||||
"ICOFileHeaders.h",
|
||||
"ImageMemoryReporter.h",
|
||||
"Resolution.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "gfxPlatform.h"
|
||||
|
||||
#include "ImageFactory.h"
|
||||
#include "imgITools.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
|
@ -831,5 +832,41 @@ ImageTestCase PerfRgbGIFTestCase() {
|
|||
return ImageTestCase("perf_srgb.gif", "image/gif", IntSize(1000, 1000));
|
||||
}
|
||||
|
||||
ImageTestCase ExifResolutionTestCase() {
|
||||
return ImageTestCase("exif_resolution.jpg", "image/jpeg", IntSize(100, 50));
|
||||
}
|
||||
|
||||
RefPtr<Image> TestCaseToDecodedImage(const ImageTestCase& aTestCase) {
|
||||
RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
|
||||
nsDependentCString(aTestCase.mMimeType));
|
||||
MOZ_RELEASE_ASSERT(!image->HasError());
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
|
||||
MOZ_RELEASE_ASSERT(inputStream);
|
||||
|
||||
// Figure out how much data we have.
|
||||
uint64_t length;
|
||||
nsresult rv = inputStream->Available(&length);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
// Write the data into the image.
|
||||
rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
|
||||
static_cast<uint32_t>(length));
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
// Let the image know we've sent all the data.
|
||||
rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
|
||||
tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
|
||||
|
||||
// Use GetFrame() to force a sync decode of the image.
|
||||
RefPtr<SourceSurface> surface = image->GetFrame(
|
||||
imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
|
||||
Unused << surface;
|
||||
return image;
|
||||
}
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -552,6 +552,11 @@ ImageTestCase DownscaledAVIFTestCase();
|
|||
ImageTestCase LargeAVIFTestCase();
|
||||
ImageTestCase MultiLayerAVIFTestCase();
|
||||
ImageTestCase TransparentAVIFTestCase();
|
||||
|
||||
ImageTestCase ExifResolutionTestCase();
|
||||
|
||||
RefPtr<Image> TestCaseToDecodedImage(const ImageTestCase&);
|
||||
|
||||
} // namespace image
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -866,37 +866,11 @@ TEST_F(ImageDecoders, AnimatedGIFWithExtraImageSubBlocks) {
|
|||
// extra data shouldn't confuse the decoder or cause the decode to fail.
|
||||
|
||||
// Create an image.
|
||||
RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
|
||||
nsDependentCString(testCase.mMimeType));
|
||||
ASSERT_TRUE(!image->HasError());
|
||||
|
||||
nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
|
||||
ASSERT_TRUE(inputStream);
|
||||
|
||||
// Figure out how much data we have.
|
||||
uint64_t length;
|
||||
nsresult rv = inputStream->Available(&length);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
// Write the data into the image.
|
||||
rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
|
||||
static_cast<uint32_t>(length));
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
// Let the image know we've sent all the data.
|
||||
rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
|
||||
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
||||
RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
|
||||
tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
|
||||
|
||||
// Use GetFrame() to force a sync decode of the image.
|
||||
RefPtr<SourceSurface> surface = image->GetFrame(
|
||||
imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
|
||||
RefPtr<Image> image = TestCaseToDecodedImage(testCase);
|
||||
|
||||
// Ensure that the image's metadata meets our expectations.
|
||||
IntSize imageSize(0, 0);
|
||||
rv = image->GetWidth(&imageSize.width);
|
||||
nsresult rv = image->GetWidth(&imageSize.width);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
rv = image->GetHeight(&imageSize.height);
|
||||
EXPECT_TRUE(NS_SUCCEEDED(rv));
|
||||
|
@ -904,6 +878,7 @@ TEST_F(ImageDecoders, AnimatedGIFWithExtraImageSubBlocks) {
|
|||
EXPECT_EQ(testCase.mSize.width, imageSize.width);
|
||||
EXPECT_EQ(testCase.mSize.height, imageSize.height);
|
||||
|
||||
RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
|
||||
Progress imageProgress = tracker->GetProgress();
|
||||
|
||||
EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
|
||||
|
@ -1028,3 +1003,8 @@ TEST_F(ImageDecoders, MultipleSizesICOSingleChunk) {
|
|||
EXPECT_EQ(expectedSizes[i], nativeSizes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ImageDecoders, ExifResolutionEven) {
|
||||
RefPtr<Image> image = TestCaseToDecodedImage(ExifResolutionTestCase());
|
||||
EXPECT_EQ(image->GetResolution(), Resolution(2.0, 2.0));
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ TEST_HARNESS_FILES.gtest += [
|
|||
"downscaled.jpg",
|
||||
"downscaled.png",
|
||||
"downscaled.webp",
|
||||
"exif_resolution.jpg",
|
||||
"first-frame-green.gif",
|
||||
"first-frame-green.png",
|
||||
"first-frame-green.webp",
|
||||
|
|
|
@ -5307,6 +5307,17 @@
|
|||
value: true
|
||||
mirror: always
|
||||
|
||||
# Whether we use EXIF metadata for image density.
|
||||
#
|
||||
# NOTE: Before shipping this, make sure that the issue described in the
|
||||
# following comment is addressed:
|
||||
#
|
||||
# https://github.com/whatwg/html/pull/5574#issuecomment-826335244
|
||||
- name: image.exif-density-correction.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: @IS_NIGHTLY_BUILD@
|
||||
mirror: always
|
||||
|
||||
# The threshold for inferring that changes to an <img> element's |src|
|
||||
# attribute by JavaScript represent an animation, in milliseconds. If the |src|
|
||||
# attribute is changing more frequently than this value, then we enter a
|
||||
|
|
Загрузка…
Ссылка в новой задаче