129bool QWebpHandler::read(QImage *image)
131 if (!ensureScanned() || !ensureDemuxer())
135 if (m_iter.frame_num == 0) {
137 WebPChunkIterator metaDataIter;
138 if ((m_formatFlags & ICCP_FLAG) && WebPDemuxGetChunk(m_demuxer,
"ICCP", 1, &metaDataIter)) {
139 QByteArray iccProfile = QByteArray::fromRawData(
reinterpret_cast<
const char *>(metaDataIter.chunk.bytes),
140 metaDataIter.chunk.size);
142 if (
reinterpret_cast<qintptr>(iccProfile.constData()) & 0x3)
144 m_colorSpace = QColorSpace::fromIccProfile(iccProfile);
146 WebPDemuxReleaseChunkIterator(&metaDataIter);
150 if (!WebPDemuxGetFrame(m_demuxer, 1, &m_iter))
153 if (m_iter.has_alpha && m_iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND)
154 prevFrameRect = currentImageRect();
157 if (!WebPDemuxNextFrame(&m_iter))
161 WebPBitstreamFeatures features;
162 VP8StatusCode status = WebPGetFeatures(m_iter.fragment.bytes, m_iter.fragment.size, &features);
163 if (status != VP8_STATUS_OK)
166 QImage::Format format = m_features.has_alpha ? QImage::Format_ARGB32 : QImage::Format_RGB32;
168 if (!QImageIOHandler::allocateImage(QSize(m_iter.width, m_iter.height), format, &frame))
170 uint8_t *output = frame.bits();
171 size_t output_size = frame.sizeInBytes();
172#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
173 if (!WebPDecodeBGRAInto(
174 reinterpret_cast<
const uint8_t*>(m_iter.fragment.bytes), m_iter.fragment.size,
175 output, output_size, frame.bytesPerLine()))
177 if (!WebPDecodeARGBInto(
178 reinterpret_cast<
const uint8_t*>(m_iter.fragment.bytes), m_iter.fragment.size,
179 output, output_size, frame.bytesPerLine()))
183 if (!m_features.has_animation) {
188 QPainter painter(m_composited);
189 if (!prevFrameRect.isEmpty()) {
190 painter.setCompositionMode(QPainter::CompositionMode_Clear);
191 painter.fillRect(prevFrameRect, Qt::black);
193 if (m_features.has_alpha) {
194 if (m_iter.blend_method == WEBP_MUX_NO_BLEND)
195 painter.setCompositionMode(QPainter::CompositionMode_Source);
197 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
199 painter.drawImage(currentImageRect(), frame);
201 *image = *m_composited;
203 image->setColorSpace(m_colorSpace);
208bool QWebpHandler::write(
const QImage &image)
210 if (image.isNull()) {
211 qWarning() <<
"source image is null.";
214 if (
std::max(image.width(), image.height()) > WEBP_MAX_DIMENSION) {
215 qWarning() <<
"QWebpHandler::write() source image too large for WebP: " << image.size();
219 const bool alpha = image.hasAlphaChannel();
220 QImage::Format newFormat = alpha ? QImage::Format_RGBA8888 : QImage::Format_RGB888;
221 const QImage srcImage = (image.format() == newFormat) ? image : image.convertedTo(newFormat);
226 if (!WebPPictureInit(&picture) || !WebPConfigInit(&config)) {
227 qWarning() <<
"failed to init webp picture and config";
231 picture.width = srcImage.width();
232 picture.height = srcImage.height();
233 picture.use_argb = 1;
236 failed = !WebPPictureImportRGBA(&picture, srcImage.constBits(), srcImage.bytesPerLine());
238 failed = !WebPPictureImportRGB(&picture, srcImage.constBits(), srcImage.bytesPerLine());
241 qWarning() <<
"failed to import image data to webp picture.";
242 WebPPictureFree(&picture);
246 int reqQuality = m_quality < 0 ? 75 : qMin(m_quality, 100);
247 if (reqQuality < 100) {
249 config.quality = reqQuality;
254 config.alpha_quality = config.quality;
255 WebPMemoryWriter writer;
256 WebPMemoryWriterInit(&writer);
257 picture.writer = WebPMemoryWrite;
258 picture.custom_ptr = &writer;
260 if (!WebPEncode(&config, &picture)) {
261 qWarning() <<
"failed to encode webp picture, error code: " << picture.error_code;
262 WebPPictureFree(&picture);
263 WebPMemoryWriterClear(&writer);
268 if (image.colorSpace().isValid()) {
270 WebPMux *mux = WebPMuxNew();
271 WebPData image_data = { writer.mem, writer.size };
272 WebPMuxSetImage(mux, &image_data, copy_data);
273 uint8_t vp8xChunk[10];
274 uint8_t flags = 0x20;
275 if (image.hasAlphaChannel())
277 vp8xChunk[0] = flags;
281 const unsigned width = image.width() - 1;
282 const unsigned height = image.height() - 1;
283 vp8xChunk[4] = width & 0xff;
284 vp8xChunk[5] = (width >> 8) & 0xff;
285 vp8xChunk[6] = (width >> 16) & 0xff;
286 vp8xChunk[7] = height & 0xff;
287 vp8xChunk[8] = (height >> 8) & 0xff;
288 vp8xChunk[9] = (height >> 16) & 0xff;
289 WebPData vp8x_data = { vp8xChunk, 10 };
290 if (WebPMuxSetChunk(mux,
"VP8X", &vp8x_data, copy_data) == WEBP_MUX_OK) {
291 QByteArray iccProfile = image.colorSpace().iccProfile();
292 WebPData iccp_data = {
293 reinterpret_cast<
const uint8_t *>(iccProfile.constData()),
294 static_cast<size_t>(iccProfile.size())
296 if (WebPMuxSetChunk(mux,
"ICCP", &iccp_data, copy_data) == WEBP_MUX_OK) {
297 WebPData output_data;
298 if (WebPMuxAssemble(mux, &output_data) == WEBP_MUX_OK) {
299 res = (output_data.size ==
300 static_cast<size_t>(
device()->write(
reinterpret_cast<
const char *>(output_data.bytes), output_data.size)));
302 WebPDataClear(&output_data);
308 res = (writer.size ==
309 static_cast<size_t>(
device()->write(
reinterpret_cast<
const char *>(writer.mem), writer.size)));
311 WebPPictureFree(&picture);
312 WebPMemoryWriterClear(&writer);