1 /* 2 * Copyright 2015 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "SkWebpCodec.h" 9 10 #include "SkBitmap.h" 11 #include "SkCanvas.h" 12 #include "SkCodecAnimation.h" 13 #include "SkCodecAnimationPriv.h" 14 #include "SkCodecPriv.h" 15 #include "SkMakeUnique.h" 16 #include "SkRasterPipeline.h" 17 #include "SkSampler.h" 18 #include "SkStreamPriv.h" 19 #include "SkTemplates.h" 20 #include "SkTo.h" 21 22 // A WebP decoder on top of (subset of) libwebp 23 // For more information on WebP image format, and libwebp library, see: 24 // https://code.google.com/speed/webp/ 25 // http://www.webmproject.org/code/#libwebp-webp-image-library 26 // https://chromium.googlesource.com/webm/libwebp 27 28 // If moving libwebp out of skia source tree, path for webp headers must be 29 // updated accordingly. Here, we enforce using local copy in webp sub-directory. 30 #include "webp/decode.h" 31 #include "webp/demux.h" 32 #include "webp/encode.h" 33 34 bool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) { 35 // WEBP starts with the following: 36 // RIFFXXXXWEBPVP 37 // Where XXXX is unspecified. 38 const char* bytes = static_cast<const char*>(buf); 39 return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6); 40 } 41 42 // Parse headers of RIFF container, and check for valid Webp (VP8) content. 43 // Returns an SkWebpCodec on success 44 std::unique_ptr<SkCodec> SkWebpCodec::MakeFromStream(std::unique_ptr<SkStream> stream, 45 Result* result) { 46 // Webp demux needs a contiguous data buffer. 47 sk_sp<SkData> data = nullptr; 48 if (stream->getMemoryBase()) { 49 // It is safe to make without copy because we'll hold onto the stream. 50 data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); 51 } else { 52 data = SkCopyStreamToData(stream.get()); 53 54 // If we are forced to copy the stream to a data, we can go ahead and delete the stream. 55 stream.reset(nullptr); 56 } 57 58 // It's a little strange that the |demux| will outlive |webpData|, though it needs the 59 // pointer in |webpData| to remain valid. This works because the pointer remains valid 60 // until the SkData is freed. 61 WebPData webpData = { data->bytes(), data->size() }; 62 WebPDemuxState state; 63 SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpData, &state)); 64 switch (state) { 65 case WEBP_DEMUX_PARSE_ERROR: 66 *result = kInvalidInput; 67 return nullptr; 68 case WEBP_DEMUX_PARSING_HEADER: 69 *result = kIncompleteInput; 70 return nullptr; 71 case WEBP_DEMUX_PARSED_HEADER: 72 case WEBP_DEMUX_DONE: 73 SkASSERT(demux); 74 break; 75 } 76 77 const int width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); 78 const int height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); 79 80 // Sanity check for image size that's about to be decoded. 81 { 82 const int64_t size = sk_64_mul(width, height); 83 // now check that if we are 4-bytes per pixel, we also don't overflow 84 if (!SkTFitsIn<int32_t>(size) || SkTo<int32_t>(size) > (0x7FFFFFFF >> 2)) { 85 *result = kInvalidInput; 86 return nullptr; 87 } 88 } 89 90 std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr; 91 { 92 WebPChunkIterator chunkIterator; 93 SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator); 94 if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) { 95 // FIXME: I think this could be MakeWithoutCopy 96 auto chunk = SkData::MakeWithCopy(chunkIterator.chunk.bytes, chunkIterator.chunk.size); 97 profile = SkEncodedInfo::ICCProfile::Make(std::move(chunk)); 98 } 99 if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) { 100 profile = nullptr; 101 } 102 } 103 104 SkEncodedOrigin origin = kDefault_SkEncodedOrigin; 105 { 106 WebPChunkIterator chunkIterator; 107 SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator); 108 if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunkIterator)) { 109 is_orientation_marker(chunkIterator.chunk.bytes, chunkIterator.chunk.size, &origin); 110 } 111 } 112 113 // Get the first frame and its "features" to determine the color and alpha types. 114 WebPIterator frame; 115 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame); 116 if (!WebPDemuxGetFrame(demux, 1, &frame)) { 117 *result = kIncompleteInput; 118 return nullptr; 119 } 120 121 WebPBitstreamFeatures features; 122 switch (WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features)) { 123 case VP8_STATUS_OK: 124 break; 125 case VP8_STATUS_SUSPENDED: 126 case VP8_STATUS_NOT_ENOUGH_DATA: 127 *result = kIncompleteInput; 128 return nullptr; 129 default: 130 *result = kInvalidInput; 131 return nullptr; 132 } 133 134 const bool hasAlpha = SkToBool(frame.has_alpha) 135 || frame.width != width || frame.height != height; 136 SkEncodedInfo::Color color; 137 SkEncodedInfo::Alpha alpha; 138 switch (features.format) { 139 case 0: 140 // This indicates a "mixed" format. We could see this for 141 // animated webps (multiple fragments). 142 // We could also guess kYUV here, but I think it makes more 143 // sense to guess kBGRA which is likely closer to the final 144 // output. Otherwise, we might end up converting 145 // BGRA->YUVA->BGRA. 146 // Fallthrough: 147 case 2: 148 // This is the lossless format (BGRA). 149 if (hasAlpha) { 150 color = SkEncodedInfo::kBGRA_Color; 151 alpha = SkEncodedInfo::kUnpremul_Alpha; 152 } else { 153 color = SkEncodedInfo::kBGRX_Color; 154 alpha = SkEncodedInfo::kOpaque_Alpha; 155 } 156 break; 157 case 1: 158 // This is the lossy format (YUV). 159 if (hasAlpha) { 160 color = SkEncodedInfo::kYUVA_Color; 161 alpha = SkEncodedInfo::kUnpremul_Alpha; 162 } else { 163 color = SkEncodedInfo::kYUV_Color; 164 alpha = SkEncodedInfo::kOpaque_Alpha; 165 } 166 break; 167 default: 168 *result = kInvalidInput; 169 return nullptr; 170 } 171 172 173 *result = kSuccess; 174 SkEncodedInfo info = SkEncodedInfo::Make(width, height, color, alpha, 8, std::move(profile)); 175 return std::unique_ptr<SkCodec>(new SkWebpCodec(std::move(info), std::move(stream), 176 demux.release(), std::move(data), origin)); 177 } 178 179 SkISize SkWebpCodec::onGetScaledDimensions(float desiredScale) const { 180 SkISize dim = this->dimensions(); 181 // SkCodec treats zero dimensional images as errors, so the minimum size 182 // that we will recommend is 1x1. 183 dim.fWidth = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fWidth)); 184 dim.fHeight = SkTMax(1, SkScalarRoundToInt(desiredScale * dim.fHeight)); 185 return dim; 186 } 187 188 bool SkWebpCodec::onDimensionsSupported(const SkISize& dim) { 189 const SkEncodedInfo& info = this->getEncodedInfo(); 190 return dim.width() >= 1 && dim.width() <= info.width() 191 && dim.height() >= 1 && dim.height() <= info.height(); 192 } 193 194 static WEBP_CSP_MODE webp_decode_mode(SkColorType dstCT, bool premultiply) { 195 switch (dstCT) { 196 case kBGRA_8888_SkColorType: 197 return premultiply ? MODE_bgrA : MODE_BGRA; 198 case kRGBA_8888_SkColorType: 199 return premultiply ? MODE_rgbA : MODE_RGBA; 200 case kRGB_565_SkColorType: 201 return MODE_RGB_565; 202 default: 203 return MODE_LAST; 204 } 205 } 206 207 SkWebpCodec::Frame* SkWebpCodec::FrameHolder::appendNewFrame(bool hasAlpha) { 208 const int i = this->size(); 209 fFrames.emplace_back(i, hasAlpha ? SkEncodedInfo::kUnpremul_Alpha 210 : SkEncodedInfo::kOpaque_Alpha); 211 return &fFrames[i]; 212 } 213 214 bool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const { 215 if (!desiredSubset) { 216 return false; 217 } 218 219 if (!this->bounds().contains(*desiredSubset)) { 220 return false; 221 } 222 223 // As stated below, libwebp snaps to even left and top. Make sure top and left are even, so we 224 // decode this exact subset. 225 // Leave right and bottom unmodified, so we suggest a slightly larger subset than requested. 226 desiredSubset->fLeft = (desiredSubset->fLeft >> 1) << 1; 227 desiredSubset->fTop = (desiredSubset->fTop >> 1) << 1; 228 return true; 229 } 230 231 int SkWebpCodec::onGetRepetitionCount() { 232 auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS); 233 if (!(flags & ANIMATION_FLAG)) { 234 return 0; 235 } 236 237 const int repCount = WebPDemuxGetI(fDemux.get(), WEBP_FF_LOOP_COUNT); 238 if (0 == repCount) { 239 return kRepetitionCountInfinite; 240 } 241 242 return repCount; 243 } 244 245 int SkWebpCodec::onGetFrameCount() { 246 auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS); 247 if (!(flags & ANIMATION_FLAG)) { 248 return 1; 249 } 250 251 const uint32_t oldFrameCount = fFrameHolder.size(); 252 if (fFailed) { 253 return oldFrameCount; 254 } 255 256 const uint32_t frameCount = WebPDemuxGetI(fDemux, WEBP_FF_FRAME_COUNT); 257 if (oldFrameCount == frameCount) { 258 // We have already parsed this. 259 return frameCount; 260 } 261 262 fFrameHolder.reserve(frameCount); 263 264 for (uint32_t i = oldFrameCount; i < frameCount; i++) { 265 WebPIterator iter; 266 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoIter(&iter); 267 268 if (!WebPDemuxGetFrame(fDemux.get(), i + 1, &iter)) { 269 fFailed = true; 270 break; 271 } 272 273 // libwebp only reports complete frames of an animated image. 274 SkASSERT(iter.complete); 275 276 Frame* frame = fFrameHolder.appendNewFrame(iter.has_alpha); 277 frame->setXYWH(iter.x_offset, iter.y_offset, iter.width, iter.height); 278 frame->setDisposalMethod(iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 279 SkCodecAnimation::DisposalMethod::kRestoreBGColor : 280 SkCodecAnimation::DisposalMethod::kKeep); 281 frame->setDuration(iter.duration); 282 if (WEBP_MUX_BLEND != iter.blend_method) { 283 frame->setBlend(SkCodecAnimation::Blend::kBG); 284 } 285 fFrameHolder.setAlphaAndRequiredFrame(frame); 286 } 287 288 return fFrameHolder.size(); 289 290 } 291 292 const SkFrame* SkWebpCodec::FrameHolder::onGetFrame(int i) const { 293 return static_cast<const SkFrame*>(this->frame(i)); 294 } 295 296 const SkWebpCodec::Frame* SkWebpCodec::FrameHolder::frame(int i) const { 297 SkASSERT(i >= 0 && i < this->size()); 298 return &fFrames[i]; 299 } 300 301 bool SkWebpCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { 302 if (i >= fFrameHolder.size()) { 303 return false; 304 } 305 306 const Frame* frame = fFrameHolder.frame(i); 307 if (!frame) { 308 return false; 309 } 310 311 if (frameInfo) { 312 frameInfo->fRequiredFrame = frame->getRequiredFrame(); 313 frameInfo->fDuration = frame->getDuration(); 314 // libwebp only reports fully received frames for an 315 // animated image. 316 frameInfo->fFullyReceived = true; 317 frameInfo->fAlphaType = frame->hasAlpha() ? kUnpremul_SkAlphaType 318 : kOpaque_SkAlphaType; 319 frameInfo->fDisposalMethod = frame->getDisposalMethod(); 320 } 321 322 return true; 323 } 324 325 static bool is_8888(SkColorType colorType) { 326 switch (colorType) { 327 case kRGBA_8888_SkColorType: 328 case kBGRA_8888_SkColorType: 329 return true; 330 default: 331 return false; 332 } 333 } 334 335 // Requires that the src input be unpremultiplied (or opaque). 336 static void blend_line(SkColorType dstCT, void* dst, 337 SkColorType srcCT, const void* src, 338 SkAlphaType dstAt, 339 bool srcHasAlpha, 340 int width) { 341 SkRasterPipeline_MemoryCtx dst_ctx = { (void*)dst, 0 }, 342 src_ctx = { (void*)src, 0 }; 343 344 SkRasterPipeline_<256> p; 345 346 p.append_load_dst(dstCT, &dst_ctx); 347 if (kUnpremul_SkAlphaType == dstAt) { 348 p.append(SkRasterPipeline::premul_dst); 349 } 350 351 p.append_load(srcCT, &src_ctx); 352 if (srcHasAlpha) { 353 p.append(SkRasterPipeline::premul); 354 } 355 356 p.append(SkRasterPipeline::srcover); 357 358 if (kUnpremul_SkAlphaType == dstAt) { 359 p.append(SkRasterPipeline::unpremul); 360 } 361 p.append_store(dstCT, &dst_ctx); 362 363 p.run(0,0, width,1); 364 } 365 366 SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, 367 const Options& options, int* rowsDecodedPtr) { 368 const int index = options.fFrameIndex; 369 SkASSERT(0 == index || index < fFrameHolder.size()); 370 SkASSERT(0 == index || !options.fSubset); 371 372 WebPDecoderConfig config; 373 if (0 == WebPInitDecoderConfig(&config)) { 374 // ABI mismatch. 375 // FIXME: New enum for this? 376 return kInvalidInput; 377 } 378 379 // Free any memory associated with the buffer. Must be called last, so we declare it first. 380 SkAutoTCallVProc<WebPDecBuffer, WebPFreeDecBuffer> autoFree(&(config.output)); 381 382 WebPIterator frame; 383 SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame); 384 // If this succeeded in onGetFrameCount(), it should succeed again here. 385 SkAssertResult(WebPDemuxGetFrame(fDemux, index + 1, &frame)); 386 387 const bool independent = index == 0 ? true : 388 (fFrameHolder.frame(index)->getRequiredFrame() == kNoFrame); 389 // Get the frameRect. libwebp will have already signaled an error if this is not fully 390 // contained by the canvas. 391 auto frameRect = SkIRect::MakeXYWH(frame.x_offset, frame.y_offset, frame.width, frame.height); 392 SkASSERT(this->bounds().contains(frameRect)); 393 const bool frameIsSubset = frameRect != this->bounds(); 394 if (independent && frameIsSubset) { 395 SkSampler::Fill(dstInfo, dst, rowBytes, options.fZeroInitialized); 396 } 397 398 int dstX = frameRect.x(); 399 int dstY = frameRect.y(); 400 int subsetWidth = frameRect.width(); 401 int subsetHeight = frameRect.height(); 402 if (options.fSubset) { 403 SkIRect subset = *options.fSubset; 404 SkASSERT(this->bounds().contains(subset)); 405 SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); 406 SkASSERT(this->getValidSubset(&subset) && subset == *options.fSubset); 407 408 if (!SkIRect::IntersectsNoEmptyCheck(subset, frameRect)) { 409 return kSuccess; 410 } 411 412 int minXOffset = SkTMin(dstX, subset.x()); 413 int minYOffset = SkTMin(dstY, subset.y()); 414 dstX -= minXOffset; 415 dstY -= minYOffset; 416 frameRect.offset(-minXOffset, -minYOffset); 417 subset.offset(-minXOffset, -minYOffset); 418 419 // Just like we require that the requested subset x and y offset are even, libwebp 420 // guarantees that the frame x and y offset are even (it's actually impossible to specify 421 // an odd frame offset). So we can still guarantee that the adjusted offsets are even. 422 SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); 423 424 SkIRect intersection; 425 SkAssertResult(intersection.intersect(frameRect, subset)); 426 subsetWidth = intersection.width(); 427 subsetHeight = intersection.height(); 428 429 config.options.use_cropping = 1; 430 config.options.crop_left = subset.x(); 431 config.options.crop_top = subset.y(); 432 config.options.crop_width = subsetWidth; 433 config.options.crop_height = subsetHeight; 434 } 435 436 // Ignore the frame size and offset when determining if scaling is necessary. 437 int scaledWidth = subsetWidth; 438 int scaledHeight = subsetHeight; 439 SkISize srcSize = options.fSubset ? options.fSubset->size() : this->dimensions(); 440 if (srcSize != dstInfo.dimensions()) { 441 config.options.use_scaling = 1; 442 443 if (frameIsSubset) { 444 float scaleX = ((float) dstInfo.width()) / srcSize.width(); 445 float scaleY = ((float) dstInfo.height()) / srcSize.height(); 446 447 // We need to be conservative here and floor rather than round. 448 // Otherwise, we may find ourselves decoding off the end of memory. 449 dstX = scaleX * dstX; 450 scaledWidth = scaleX * scaledWidth; 451 dstY = scaleY * dstY; 452 scaledHeight = scaleY * scaledHeight; 453 if (0 == scaledWidth || 0 == scaledHeight) { 454 return kSuccess; 455 } 456 } else { 457 scaledWidth = dstInfo.width(); 458 scaledHeight = dstInfo.height(); 459 } 460 461 config.options.scaled_width = scaledWidth; 462 config.options.scaled_height = scaledHeight; 463 } 464 465 const bool blendWithPrevFrame = !independent && frame.blend_method == WEBP_MUX_BLEND 466 && frame.has_alpha; 467 468 SkBitmap webpDst; 469 auto webpInfo = dstInfo; 470 if (!frame.has_alpha) { 471 webpInfo = webpInfo.makeAlphaType(kOpaque_SkAlphaType); 472 } 473 if (this->colorXform()) { 474 // Swizzling between RGBA and BGRA is zero cost in a color transform. So when we have a 475 // color transform, we should decode to whatever is easiest for libwebp, and then let the 476 // color transform swizzle if necessary. 477 // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost). Lossless webp is 478 // encoded as BGRA. This means decoding to BGRA is either faster or the same cost as RGBA. 479 webpInfo = webpInfo.makeColorType(kBGRA_8888_SkColorType); 480 481 if (webpInfo.alphaType() == kPremul_SkAlphaType) { 482 webpInfo = webpInfo.makeAlphaType(kUnpremul_SkAlphaType); 483 } 484 } 485 486 if ((this->colorXform() && !is_8888(dstInfo.colorType())) || blendWithPrevFrame) { 487 // We will decode the entire image and then perform the color transform. libwebp 488 // does not provide a row-by-row API. This is a shame particularly when we do not want 489 // 8888, since we will need to create another image sized buffer. 490 webpDst.allocPixels(webpInfo); 491 } else { 492 // libwebp can decode directly into the output memory. 493 webpDst.installPixels(webpInfo, dst, rowBytes); 494 } 495 496 config.output.colorspace = webp_decode_mode(webpInfo.colorType(), 497 frame.has_alpha && dstInfo.alphaType() == kPremul_SkAlphaType && !this->colorXform()); 498 config.output.is_external_memory = 1; 499 500 config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY)); 501 config.output.u.RGBA.stride = static_cast<int>(webpDst.rowBytes()); 502 config.output.u.RGBA.size = webpDst.computeByteSize(); 503 504 SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &config)); 505 if (!idec) { 506 return kInvalidInput; 507 } 508 509 int rowsDecoded = 0; 510 SkCodec::Result result; 511 switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) { 512 case VP8_STATUS_OK: 513 rowsDecoded = scaledHeight; 514 result = kSuccess; 515 break; 516 case VP8_STATUS_SUSPENDED: 517 if (!WebPIDecGetRGB(idec, &rowsDecoded, nullptr, nullptr, nullptr) 518 || rowsDecoded <= 0) { 519 return kInvalidInput; 520 } 521 *rowsDecodedPtr = rowsDecoded + dstY; 522 result = kIncompleteInput; 523 break; 524 default: 525 return kInvalidInput; 526 } 527 528 const size_t dstBpp = dstInfo.bytesPerPixel(); 529 dst = SkTAddOffset<void>(dst, dstBpp * dstX + rowBytes * dstY); 530 const size_t srcRowBytes = config.output.u.RGBA.stride; 531 532 const auto dstCT = dstInfo.colorType(); 533 if (this->colorXform()) { 534 uint32_t* xformSrc = (uint32_t*) config.output.u.RGBA.rgba; 535 SkBitmap tmp; 536 void* xformDst; 537 538 if (blendWithPrevFrame) { 539 // Xform into temporary bitmap big enough for one row. 540 tmp.allocPixels(dstInfo.makeWH(scaledWidth, 1)); 541 xformDst = tmp.getPixels(); 542 } else { 543 xformDst = dst; 544 } 545 546 for (int y = 0; y < rowsDecoded; y++) { 547 this->applyColorXform(xformDst, xformSrc, scaledWidth); 548 if (blendWithPrevFrame) { 549 blend_line(dstCT, dst, dstCT, xformDst, 550 dstInfo.alphaType(), frame.has_alpha, scaledWidth); 551 dst = SkTAddOffset<void>(dst, rowBytes); 552 } else { 553 xformDst = SkTAddOffset<void>(xformDst, rowBytes); 554 } 555 xformSrc = SkTAddOffset<uint32_t>(xformSrc, srcRowBytes); 556 } 557 } else if (blendWithPrevFrame) { 558 const uint8_t* src = config.output.u.RGBA.rgba; 559 560 for (int y = 0; y < rowsDecoded; y++) { 561 blend_line(dstCT, dst, webpDst.colorType(), src, 562 dstInfo.alphaType(), frame.has_alpha, scaledWidth); 563 src = SkTAddOffset<const uint8_t>(src, srcRowBytes); 564 dst = SkTAddOffset<void>(dst, rowBytes); 565 } 566 } 567 568 return result; 569 } 570 571 SkWebpCodec::SkWebpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream, 572 WebPDemuxer* demux, sk_sp<SkData> data, SkEncodedOrigin origin) 573 : INHERITED(std::move(info), skcms_PixelFormat_BGRA_8888, std::move(stream), 574 origin) 575 , fDemux(demux) 576 , fData(std::move(data)) 577 , fFailed(false) 578 { 579 const auto& eInfo = this->getEncodedInfo(); 580 fFrameHolder.setScreenSize(eInfo.width(), eInfo.height()); 581 } 582