133bool QWebpHandler::read(QImage *image)
135 if (!ensureScanned() || !ensureDemuxer())
139 if (m_iter.frame_num == 0) {
141 WebPChunkIterator metaDataIter;
142 if ((m_formatFlags & ICCP_FLAG) && WebPDemuxGetChunk(m_demuxer,
"ICCP", 1, &metaDataIter)) {
143 QByteArray iccProfile = QByteArray::fromRawData(
reinterpret_cast<
const char *>(metaDataIter.chunk.bytes),
144 metaDataIter.chunk.size);
146 if (
reinterpret_cast<qintptr>(iccProfile.constData()) & 0x3)
148 m_colorSpace = QColorSpace::fromIccProfile(iccProfile);
150 WebPDemuxReleaseChunkIterator(&metaDataIter);
154 if (!WebPDemuxGetFrame(m_demuxer, 1, &m_iter))
157 if (m_iter.has_alpha && m_iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND)
158 prevFrameRect = currentImageRect();
161 if (!WebPDemuxNextFrame(&m_iter))
165 WebPBitstreamFeatures features;
166 VP8StatusCode status = WebPGetFeatures(m_iter.fragment.bytes, m_iter.fragment.size, &features);
167 if (status != VP8_STATUS_OK)
170 QImage::Format format = m_features.has_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
172 if (!QImageIOHandler::allocateImage(QSize(m_iter.width, m_iter.height), format, &frame))
174 uint8_t *output = frame.bits();
175 size_t output_size = frame.sizeInBytes();
176#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
177 if (!WebPDecodeBGRAInto(
178 reinterpret_cast<
const uint8_t*>(m_iter.fragment.bytes), m_iter.fragment.size,
179 output, output_size, frame.bytesPerLine()))
181 if (!WebPDecodeARGBInto(
182 reinterpret_cast<
const uint8_t*>(m_iter.fragment.bytes), m_iter.fragment.size,
183 output, output_size, frame.bytesPerLine()))
187 if (m_features.has_animation) {
189 QPainter painter(m_composited);
190 if (!prevFrameRect.isEmpty()) {
191 painter.setCompositionMode(QPainter::CompositionMode_Clear);
192 painter.fillRect(prevFrameRect, Qt::black);
194 if (m_features.has_alpha) {
195 if (m_iter.blend_method == WEBP_MUX_NO_BLEND)
196 painter.setCompositionMode(QPainter::CompositionMode_Source);
198 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
200 painter.drawImage(currentImageRect(), frame);
202 *image = *m_composited;
205 *image = std::move(frame);
207 image->setColorSpace(m_colorSpace);
212bool QWebpHandler::write(
const QImage &image)
214 if (image.isNull()) {
215 qWarning() <<
"source image is null.";
218 if (
std::max(image.width(), image.height()) > WEBP_MAX_DIMENSION) {
219 qWarning() <<
"QWebpHandler::write() source image too large for WebP: " << image.size();
223 const bool alpha = image.hasAlphaChannel();
224 QImage::Format newFormat = alpha ? QImage::Format_RGBA8888 : QImage::Format_RGB888;
225 const QImage srcImage = (image.format() == newFormat) ? image : image.convertedTo(newFormat);
230 if (!WebPPictureInit(&picture) || !WebPConfigInit(&config)) {
231 qWarning() <<
"failed to init webp picture and config";
235 picture.width = srcImage.width();
236 picture.height = srcImage.height();
237 picture.use_argb = 1;
240 failed = !WebPPictureImportRGBA(&picture, srcImage.constBits(), srcImage.bytesPerLine());
242 failed = !WebPPictureImportRGB(&picture, srcImage.constBits(), srcImage.bytesPerLine());
245 qWarning() <<
"failed to import image data to webp picture.";
246 WebPPictureFree(&picture);
250 int reqQuality = m_quality < 0 ? 75 : qMin(m_quality, 100);
251 if (reqQuality < 100) {
253 config.quality = reqQuality;
258 config.alpha_quality = config.quality;
259 WebPMemoryWriter writer;
260 WebPMemoryWriterInit(&writer);
261 picture.writer = WebPMemoryWrite;
262 picture.custom_ptr = &writer;
264 if (!WebPEncode(&config, &picture)) {
265 qWarning() <<
"failed to encode webp picture, error code: " << picture.error_code;
266 WebPPictureFree(&picture);
267 WebPMemoryWriterClear(&writer);
272 if (image.colorSpace().isValid()) {
274 WebPMux *mux = WebPMuxNew();
275 WebPData image_data = { writer.mem, writer.size };
276 WebPMuxSetImage(mux, &image_data, copy_data);
277 uint8_t vp8xChunk[10];
278 uint8_t flags = 0x20;
279 if (image.hasAlphaChannel())
281 vp8xChunk[0] = flags;
285 const unsigned width = image.width() - 1;
286 const unsigned height = image.height() - 1;
287 vp8xChunk[4] = width & 0xff;
288 vp8xChunk[5] = (width >> 8) & 0xff;
289 vp8xChunk[6] = (width >> 16) & 0xff;
290 vp8xChunk[7] = height & 0xff;
291 vp8xChunk[8] = (height >> 8) & 0xff;
292 vp8xChunk[9] = (height >> 16) & 0xff;
293 WebPData vp8x_data = { vp8xChunk, 10 };
294 if (WebPMuxSetChunk(mux,
"VP8X", &vp8x_data, copy_data) == WEBP_MUX_OK) {
295 QByteArray iccProfile = image.colorSpace().iccProfile();
296 WebPData iccp_data = {
297 reinterpret_cast<
const uint8_t *>(iccProfile.constData()),
298 static_cast<size_t>(iccProfile.size())
300 if (WebPMuxSetChunk(mux,
"ICCP", &iccp_data, copy_data) == WEBP_MUX_OK) {
301 WebPData output_data;
302 if (WebPMuxAssemble(mux, &output_data) == WEBP_MUX_OK) {
303 res = (output_data.size ==
304 static_cast<size_t>(
device()->write(
reinterpret_cast<
const char *>(output_data.bytes), output_data.size)));
306 WebPDataClear(&output_data);
312 res = (writer.size ==
313 static_cast<size_t>(
device()->write(
reinterpret_cast<
const char *>(writer.mem), writer.size)));
315 WebPPictureFree(&picture);
316 WebPMemoryWriterClear(&writer);