1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "services/data_decoder/public/cpp/decode_image.h"
6 
7 #include <utility>
8 
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/debug/dump_without_crashing.h"
12 #include "mojo/public/cpp/bindings/remote.h"
13 #include "services/data_decoder/public/cpp/data_decoder.h"
14 #include "skia/ext/skia_utils_base.h"
15 #include "third_party/skia/include/core/SkBitmap.h"
16 
17 namespace data_decoder {
18 
19 namespace {
20 
21 // Helper callback which owns a mojo::Remote<ImageDecoder> until invoked. This
22 // keeps the ImageDecoder pipe open just long enough to dispatch a reply, at
23 // which point the reply is forwarded to the wrapped |callback|.
OnDecodeImage(mojo::Remote<mojom::ImageDecoder> decoder,mojom::ImageDecoder::DecodeImageCallback callback,const SkBitmap & unsafe_bitmap)24 void OnDecodeImage(mojo::Remote<mojom::ImageDecoder> decoder,
25                    mojom::ImageDecoder::DecodeImageCallback callback,
26                    const SkBitmap& unsafe_bitmap) {
27   // On receipt of an arbitrary bitmap from the renderer, we convert to an N32
28   // 32bpp bitmap. Other pixel sizes can lead to out-of-bounds mistakes when
29   // transferring the pixels out of the bitmap into other buffers.
30   SkBitmap bitmap;
31   if (!skia::SkBitmapToN32OpaqueOrPremul(unsafe_bitmap, &bitmap)) {
32     NOTREACHED() << "Unable to convert bitmap for decode image";
33     base::debug::DumpWithoutCrashing();
34     std::move(callback).Run(SkBitmap());
35     return;
36   }
37   std::move(callback).Run(bitmap);
38 }
39 
OnDecodeImages(mojo::Remote<mojom::ImageDecoder> decoder,mojom::ImageDecoder::DecodeAnimationCallback callback,std::vector<mojom::AnimationFramePtr> bitmaps)40 void OnDecodeImages(mojo::Remote<mojom::ImageDecoder> decoder,
41                     mojom::ImageDecoder::DecodeAnimationCallback callback,
42                     std::vector<mojom::AnimationFramePtr> bitmaps) {
43   for (mojom::AnimationFramePtr& frame : bitmaps) {
44     if (frame->bitmap.colorType() != kN32_SkColorType) {
45       // The renderer should be sending us N32 32bpp bitmaps in reply, otherwise
46       // this can lead to out-of-bounds mistakes when transferring the pixels
47       // out of the bitmap into other buffers.
48       base::debug::DumpWithoutCrashing();
49       std::move(callback).Run(std::vector<mojom::AnimationFramePtr>());
50       return;
51     }
52   }
53   std::move(callback).Run(std::move(bitmaps));
54 }
55 
56 }  // namespace
57 
DecodeImageIsolated(const std::vector<uint8_t> & encoded_bytes,mojom::ImageCodec codec,bool shrink_to_fit,uint64_t max_size_in_bytes,const gfx::Size & desired_image_frame_size,mojom::ImageDecoder::DecodeImageCallback callback)58 void DecodeImageIsolated(const std::vector<uint8_t>& encoded_bytes,
59                          mojom::ImageCodec codec,
60                          bool shrink_to_fit,
61                          uint64_t max_size_in_bytes,
62                          const gfx::Size& desired_image_frame_size,
63                          mojom::ImageDecoder::DecodeImageCallback callback) {
64   // Create a new DataDecoder that we keep alive until |callback| is invoked.
65   auto data_decoder = std::make_unique<DataDecoder>();
66   auto* raw_decoder = data_decoder.get();
67   auto wrapped_callback = base::BindOnce(
68       [](std::unique_ptr<DataDecoder>,
69          mojom::ImageDecoder::DecodeImageCallback callback,
70          const SkBitmap& bitmap) { std::move(callback).Run(bitmap); },
71       std::move(data_decoder), std::move(callback));
72   DecodeImage(raw_decoder, encoded_bytes, codec, shrink_to_fit,
73               max_size_in_bytes, desired_image_frame_size,
74               std::move(wrapped_callback));
75 }
76 
DecodeImage(DataDecoder * data_decoder,const std::vector<uint8_t> & encoded_bytes,mojom::ImageCodec codec,bool shrink_to_fit,uint64_t max_size_in_bytes,const gfx::Size & desired_image_frame_size,mojom::ImageDecoder::DecodeImageCallback callback)77 void DecodeImage(DataDecoder* data_decoder,
78                  const std::vector<uint8_t>& encoded_bytes,
79                  mojom::ImageCodec codec,
80                  bool shrink_to_fit,
81                  uint64_t max_size_in_bytes,
82                  const gfx::Size& desired_image_frame_size,
83                  mojom::ImageDecoder::DecodeImageCallback callback) {
84   mojo::Remote<mojom::ImageDecoder> decoder;
85   data_decoder->GetService()->BindImageDecoder(
86       decoder.BindNewPipeAndPassReceiver());
87 
88   // |call_once| runs |callback| on its first invocation.
89   auto call_once = base::AdaptCallbackForRepeating(std::move(callback));
90   decoder.set_disconnect_handler(base::BindOnce(call_once, SkBitmap()));
91 
92   mojom::ImageDecoder* raw_decoder = decoder.get();
93   raw_decoder->DecodeImage(
94       encoded_bytes, codec, shrink_to_fit, max_size_in_bytes,
95       desired_image_frame_size,
96       base::BindOnce(&OnDecodeImage, std::move(decoder), std::move(call_once)));
97 }
98 
DecodeAnimationIsolated(const std::vector<uint8_t> & encoded_bytes,bool shrink_to_fit,uint64_t max_size_in_bytes,mojom::ImageDecoder::DecodeAnimationCallback callback)99 void DecodeAnimationIsolated(
100     const std::vector<uint8_t>& encoded_bytes,
101     bool shrink_to_fit,
102     uint64_t max_size_in_bytes,
103     mojom::ImageDecoder::DecodeAnimationCallback callback) {
104   // Create a new DataDecoder that we keep alive until |callback| is invoked.
105   auto decoder = std::make_unique<DataDecoder>();
106   auto* raw_decoder = decoder.get();
107   auto wrapped_callback = base::BindOnce(
108       [](std::unique_ptr<DataDecoder>,
109          mojom::ImageDecoder::DecodeAnimationCallback callback,
110          std::vector<mojom::AnimationFramePtr> frames) {
111         std::move(callback).Run(std::move(frames));
112       },
113       std::move(decoder), std::move(callback));
114   DecodeAnimation(raw_decoder, encoded_bytes, shrink_to_fit, max_size_in_bytes,
115                   std::move(wrapped_callback));
116 }
117 
DecodeAnimation(DataDecoder * data_decoder,const std::vector<uint8_t> & encoded_bytes,bool shrink_to_fit,uint64_t max_size_in_bytes,mojom::ImageDecoder::DecodeAnimationCallback callback)118 void DecodeAnimation(DataDecoder* data_decoder,
119                      const std::vector<uint8_t>& encoded_bytes,
120                      bool shrink_to_fit,
121                      uint64_t max_size_in_bytes,
122                      mojom::ImageDecoder::DecodeAnimationCallback callback) {
123   mojo::Remote<mojom::ImageDecoder> decoder;
124   data_decoder->GetService()->BindImageDecoder(
125       decoder.BindNewPipeAndPassReceiver());
126 
127   // |call_once| runs |callback| on its first invocation.
128   auto call_once = base::AdaptCallbackForRepeating(std::move(callback));
129   decoder.set_disconnect_handler(
130       base::BindOnce(call_once, std::vector<mojom::AnimationFramePtr>()));
131 
132   mojom::ImageDecoder* raw_decoder = decoder.get();
133   raw_decoder->DecodeAnimation(
134       encoded_bytes, shrink_to_fit, max_size_in_bytes,
135       base::BindOnce(&OnDecodeImages, std::move(decoder),
136                      std::move(call_once)));
137 }
138 
139 }  // namespace data_decoder
140