diff options
author | Eirik Aavitsland <eirik.aavitsland@theqtcompany.com> | 2016-03-08 10:28:34 +0100 |
---|---|---|
committer | aavit <eirik.aavitsland@theqtcompany.com> | 2016-03-17 12:56:31 +0000 |
commit | 2d59367241aafbfe6acb30202f64e30697028bd9 (patch) | |
tree | ee121baef55df5e1b2663f8f6948b88de9d703b4 /src | |
parent | a71771a1d7ac6b83e4ddd4163e9797f440f2f3fb (diff) | |
download | qtimageformats-2d59367241aafbfe6acb30202f64e30697028bd9.tar.gz |
Add a Jpeg2000 handler that utilizes Apple ImageIO Framework
The Jasper-based jp2 plugin has been disabled by default.
This plugin can replace it for OS X and iOS.
Change-Id: Id43dbefdf7df22493910a855f823c9b966bafcdb
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@theqtcompany.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/imageformats/imageformats.pro | 11 | ||||
-rw-r--r-- | src/plugins/imageformats/macjp2/macjp2.json | 4 | ||||
-rw-r--r-- | src/plugins/imageformats/macjp2/macjp2.pro | 20 | ||||
-rw-r--r-- | src/plugins/imageformats/macjp2/main.cpp | 91 | ||||
-rw-r--r-- | src/plugins/imageformats/macjp2/qiiofhelpers.cpp | 169 | ||||
-rw-r--r-- | src/plugins/imageformats/macjp2/qiiofhelpers_p.h | 72 | ||||
-rw-r--r-- | src/plugins/imageformats/macjp2/qmacjp2handler.cpp | 127 | ||||
-rw-r--r-- | src/plugins/imageformats/macjp2/qmacjp2handler.h | 77 |
8 files changed, 567 insertions, 4 deletions
diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro index 1ed399a..08f5151 100644 --- a/src/plugins/imageformats/imageformats.pro +++ b/src/plugins/imageformats/imageformats.pro @@ -7,14 +7,17 @@ SUBDIRS = \ wbmp \ webp -config_jasper: SUBDIRS += jp2 config_libmng: SUBDIRS += mng +config_jasper { + SUBDIRS += jp2 +} else:darwin: { + SUBDIRS += macjp2 +} wince:SUBDIRS -= jp2 winrt { SUBDIRS -= tiff \ - tga + tga \ + webp } - -winrt: SUBDIRS -= webp diff --git a/src/plugins/imageformats/macjp2/macjp2.json b/src/plugins/imageformats/macjp2/macjp2.json new file mode 100644 index 0000000..e3d7fe3 --- /dev/null +++ b/src/plugins/imageformats/macjp2/macjp2.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "jp2" ], + "MimeTypes": [ "image/jp2", "image/jpx", "image/jpm", "video/mj2" ] +} diff --git a/src/plugins/imageformats/macjp2/macjp2.pro b/src/plugins/imageformats/macjp2/macjp2.pro new file mode 100644 index 0000000..39319d8 --- /dev/null +++ b/src/plugins/imageformats/macjp2/macjp2.pro @@ -0,0 +1,20 @@ +TARGET = qmacjp2 +PLUGIN_TYPE = imageformats +PLUGIN_CLASS_NAME = QMacJp2Plugin + +LIBS += -framework Cocoa -framework Carbon -framework IOKit -framework ImageIO + +QT += core-private gui-private platformsupport-private + +SOURCES += \ + qmacjp2handler.cpp \ + main.cpp \ + qiiofhelpers.cpp + +HEADERS += \ + qmacjp2handler.h \ + qiiofhelpers_p.h + +OTHER_FILES += macjp2.json + +load(qt_plugin) diff --git a/src/plugins/imageformats/macjp2/main.cpp b/src/plugins/imageformats/macjp2/main.cpp new file mode 100644 index 0000000..95f7175 --- /dev/null +++ b/src/plugins/imageformats/macjp2/main.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the MacJp2 plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#include "qmacjp2handler.h" + +QT_BEGIN_NAMESPACE + +class QMacJp2Plugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "macjp2.json") + +public: + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QStringList QMacJp2Plugin::keys() const +{ + return QStringList() << QStringLiteral("jp2"); +} + +QImageIOPlugin::Capabilities QMacJp2Plugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "jp2") + return Capabilities(CanRead | CanWrite); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QMacJp2Handler::canRead(device)) + cap |= CanRead; + if (device->isWritable()) + cap |= CanWrite; + return cap; +} + +QImageIOHandler *QMacJp2Plugin::create(QIODevice *device, const QByteArray &format) const +{ + QMacJp2Handler *handler = new QMacJp2Handler(); + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +QT_END_NAMESPACE + +#include "main.moc" + +#endif // !QT_NO_IMAGEFORMATPLUGIN diff --git a/src/plugins/imageformats/macjp2/qiiofhelpers.cpp b/src/plugins/imageformats/macjp2/qiiofhelpers.cpp new file mode 100644 index 0000000..99903b0 --- /dev/null +++ b/src/plugins/imageformats/macjp2/qiiofhelpers.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the MacJp2 plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <qpa/qplatformnativeinterface.h> +#include <QBuffer> +#include <QImageIOHandler> +#include <QImage> +#include <private/qcore_mac_p.h> + +#include "qiiofhelpers_p.h" + +#include <ImageIO/ImageIO.h> + +QT_BEGIN_NAMESPACE + +// Callbacks for sequential data provider & consumer: + +static size_t cbGetBytes(void *info, void *buffer, size_t count) +{ + QIODevice *dev = static_cast<QIODevice *>(info); + if (!dev || !buffer) + return 0; + qint64 res = dev->read(static_cast<char *>(buffer), count); + return qMax(qint64(0), res); +} + +static off_t cbSkipForward(void *info, off_t count) +{ + QIODevice *dev = static_cast<QIODevice *>(info); + if (!dev || !count) + return 0; + qint64 res = 0; + if (!dev->isSequential()) { + qint64 prevPos = dev->pos(); + dev->seek(prevPos + count); + res = dev->pos() - prevPos; + } else { + char *buf = new char[count]; + res = dev->read(buf, count); + delete buf; + } + return qMax(qint64(0), res); +} + +static void cbRewind(void *) +{ + // Ignore this; we do not want the Qt device to be rewound after reading the image +} + +static size_t cbPutBytes(void *info, const void *buffer, size_t count) +{ + QIODevice *dev = static_cast<QIODevice *>(info); + if (!dev || !buffer) + return 0; + qint64 res = dev->write(static_cast<const char *>(buffer), count); + return qMax(qint64(0), res); +} + + +// QImage <-> CGImage conversion functions +typedef QImage (*cgImageToQImagePtr)(CGImageRef image); +typedef CGImageRef (*qImageToCGImagePtr)(const QImage &image); + +bool QIIOFHelpers::readImage(QImageIOHandler *q_ptr, QImage *out) +{ + static const CGDataProviderSequentialCallbacks cgCallbacks = { 0, &cbGetBytes, &cbSkipForward, &cbRewind, nullptr }; + static cgImageToQImagePtr cgImageToQImageFn = nullptr; + if (!cgImageToQImageFn) { + if (QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface()) + cgImageToQImageFn = reinterpret_cast<cgImageToQImagePtr>(pni->nativeResourceFunctionForIntegration(QByteArrayLiteral("cgImageToQImage"))); + } + + if (!q_ptr || !q_ptr->device() || !out || !cgImageToQImageFn) + return false; + + QCFType<CGDataProviderRef> cgDataProvider; + if (QBuffer *b = qobject_cast<QBuffer *>(q_ptr->device())) { + // do direct access to avoid data copy + const void *rawData = b->data().constData() + b->pos(); + cgDataProvider = CGDataProviderCreateWithData(nullptr, rawData, b->data().size() - b->pos(), nullptr); + } else { + cgDataProvider = CGDataProviderCreateSequential(q_ptr->device(), &cgCallbacks); + } + + QCFType<CGImageSourceRef> cgImageSource = CGImageSourceCreateWithDataProvider(cgDataProvider, nullptr); + if (!cgImageSource) + return false; + + QCFType<CGImageRef> cgImage = CGImageSourceCreateImageAtIndex(cgImageSource, 0, nullptr); + if (!cgImage) + return false; + + *out = cgImageToQImageFn(cgImage); + return !out->isNull(); +} + + +bool QIIOFHelpers::writeImage(QImageIOHandler *q_ptr, const QImage &in, const QString &uti) +{ + static const CGDataConsumerCallbacks cgCallbacks = { &cbPutBytes, nullptr }; + static qImageToCGImagePtr qImageToCGImageFn = nullptr; + if (!qImageToCGImageFn) { + if (QPlatformNativeInterface *pni = QGuiApplication::platformNativeInterface()) + qImageToCGImageFn = reinterpret_cast<qImageToCGImagePtr>(pni->nativeResourceFunctionForIntegration(QByteArrayLiteral("qImageToCGImage"))); + } + + if (!q_ptr || !q_ptr->device() || in.isNull() || !qImageToCGImageFn) + return false; + + QCFType<CGImageRef> cgImage = qImageToCGImageFn(in); + QCFType<CGDataConsumerRef> cgDataConsumer = CGDataConsumerCreate(q_ptr->device(), &cgCallbacks); + QCFType<CFStringRef> cfUti = uti.toCFString(); + QCFType<CGImageDestinationRef> cgImageDest = CGImageDestinationCreateWithDataConsumer(cgDataConsumer, cfUti, 1, nullptr); + if (!cgImageDest || !cgImage) + return false; + + QCFType<CFNumberRef> cfVal; + QCFType<CFDictionaryRef> cfProps; + if (q_ptr->supportsOption(QImageIOHandler::Quality)) { + bool ok = false; + int writeQuality = q_ptr->option(QImageIOHandler::Quality).toInt(&ok); + // If quality is unset, default to 75% + float quality = (ok && writeQuality >= 0 ? (qMin(writeQuality, 100)) : 75) / 100.0; + cfVal = CFNumberCreate(nullptr, kCFNumberFloatType, &quality); + cfProps = CFDictionaryCreate(nullptr, (const void **)&kCGImageDestinationLossyCompressionQuality, (const void **)&cfVal, 1, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + CGImageDestinationAddImage(cgImageDest, cgImage, cfProps); + return CGImageDestinationFinalize(cgImageDest); +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/macjp2/qiiofhelpers_p.h b/src/plugins/imageformats/macjp2/qiiofhelpers_p.h new file mode 100644 index 0000000..ef5a022 --- /dev/null +++ b/src/plugins/imageformats/macjp2/qiiofhelpers_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the MacJp2 plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QIIOFHELPERS_P_H +#define QIIOFHELPERS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QImageIOHandler; +class QImage; + +/* +Functions to utilize the native ImageIO Framework in OS X and iOS +*/ + +class QIIOFHelpers +{ +public: + static bool readImage(QImageIOHandler *q_ptr, QImage *out); + static bool writeImage(QImageIOHandler *q_ptr, const QImage &in, const QString &uti); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/imageformats/macjp2/qmacjp2handler.cpp b/src/plugins/imageformats/macjp2/qmacjp2handler.cpp new file mode 100644 index 0000000..72f753a --- /dev/null +++ b/src/plugins/imageformats/macjp2/qmacjp2handler.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the MacJp2 plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmacjp2handler.h" +#include "qiiofhelpers_p.h" +#include <QVariant> + +QT_BEGIN_NAMESPACE + +class QMacJp2HandlerPrivate +{ + Q_DECLARE_PUBLIC(QMacJp2Handler) + Q_DISABLE_COPY(QMacJp2HandlerPrivate) +public: + QMacJp2HandlerPrivate(QMacJp2Handler *q_ptr) + : writeQuality(-1), q_ptr(q_ptr) + {} + + int writeQuality; + QMacJp2Handler *q_ptr; +}; + + +QMacJp2Handler::QMacJp2Handler() + : d_ptr(new QMacJp2HandlerPrivate(this)) +{ +} + +QMacJp2Handler::~QMacJp2Handler() +{ +} + +bool QMacJp2Handler::canRead(QIODevice *iod) +{ + bool bCanRead = false; + char buf[12]; + if (iod && iod->peek(buf, 12) == 12) + bCanRead = !qstrncmp(buf, "\000\000\000\fjP \r\n\207\n", 12); + return bCanRead; +} + +bool QMacJp2Handler::canRead() const +{ + if (canRead(device())) { + setFormat("jp2"); + return true; + } + return false; +} + +bool QMacJp2Handler::read(QImage *image) +{ + return QIIOFHelpers::readImage(this, image); +} + +bool QMacJp2Handler::write(const QImage &image) +{ + return QIIOFHelpers::writeImage(this, image, QStringLiteral("public.jpeg-2000")); +} + +QVariant QMacJp2Handler::option(ImageOption option) const +{ + Q_D(const QMacJp2Handler); + if (option == Quality) + return QVariant(d->writeQuality); + return QVariant(); +} + +void QMacJp2Handler::setOption(ImageOption option, const QVariant &value) +{ + Q_D(QMacJp2Handler); + if (option == Quality) { + bool ok; + const int quality = value.toInt(&ok); + if (ok) + d->writeQuality = quality; + } +} + +bool QMacJp2Handler::supportsOption(ImageOption option) const +{ + return (option == Quality); +} + +QByteArray QMacJp2Handler::name() const +{ + return QByteArrayLiteral("jp2"); +} + + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/macjp2/qmacjp2handler.h b/src/plugins/imageformats/macjp2/qmacjp2handler.h new file mode 100644 index 0000000..3706d98 --- /dev/null +++ b/src/plugins/imageformats/macjp2/qmacjp2handler.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the MacJp2 plugin in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMACJP2HANDLER_H +#define QMACJP2HANDLER_H + +#include <QScopedPointer> +#include <QImageIOHandler> + +QT_BEGIN_NAMESPACE + +class QImage; +class QByteArray; +class QIODevice; +class QVariant; +class QMacJp2HandlerPrivate; + +class QMacJp2Handler : public QImageIOHandler +{ +public: + QMacJp2Handler(); + virtual ~QMacJp2Handler(); + + bool canRead() const Q_DECL_OVERRIDE; + bool read(QImage *image) Q_DECL_OVERRIDE; + bool write(const QImage &image) Q_DECL_OVERRIDE; + QVariant option(ImageOption option) const Q_DECL_OVERRIDE; + void setOption(ImageOption option, const QVariant &value) Q_DECL_OVERRIDE; + bool supportsOption(ImageOption option) const Q_DECL_OVERRIDE; + QByteArray name() const Q_DECL_OVERRIDE; + + static bool canRead(QIODevice *iod); + +private: + Q_DECLARE_PRIVATE(QMacJp2Handler) + QScopedPointer<QMacJp2HandlerPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QMACJP2HANDLER_P_H |