1 /*
2  * Copyright (C) 2013 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/platform/graphics/picture_snapshot.h"
32 
33 #include <memory>
34 #include "third_party/blink/renderer/platform/geometry/int_size.h"
35 #include "third_party/blink/renderer/platform/graphics/logging_canvas.h"
36 #include "third_party/blink/renderer/platform/graphics/profiling_canvas.h"
37 #include "third_party/blink/renderer/platform/graphics/replaying_canvas.h"
38 #include "third_party/blink/renderer/platform/graphics/skia/image_pixel_locker.h"
39 #include "third_party/blink/renderer/platform/image-decoders/image_decoder.h"
40 #include "third_party/blink/renderer/platform/image-decoders/image_frame.h"
41 #include "third_party/blink/renderer/platform/image-decoders/segment_reader.h"
42 #include "third_party/blink/renderer/platform/image-encoders/image_encoder.h"
43 #include "third_party/blink/renderer/platform/wtf/text/text_encoding.h"
44 
45 #include "third_party/skia/include/core/SkImage.h"
46 #include "third_party/skia/include/core/SkPictureRecorder.h"
47 
48 namespace blink {
49 
PictureSnapshot(sk_sp<const SkPicture> picture)50 PictureSnapshot::PictureSnapshot(sk_sp<const SkPicture> picture)
51     : picture_(std::move(picture)) {}
52 
Load(const Vector<scoped_refptr<TilePictureStream>> & tiles)53 scoped_refptr<PictureSnapshot> PictureSnapshot::Load(
54     const Vector<scoped_refptr<TilePictureStream>>& tiles) {
55   DCHECK(!tiles.IsEmpty());
56   Vector<sk_sp<SkPicture>> pictures;
57   pictures.ReserveCapacity(tiles.size());
58   FloatRect union_rect;
59   for (const auto& tile_stream : tiles) {
60     sk_sp<SkPicture> picture = std::move(tile_stream->picture);
61     if (!picture)
62       return nullptr;
63     FloatRect cull_rect(picture->cullRect());
64     cull_rect.MoveBy(tile_stream->layer_offset);
65     union_rect.Unite(cull_rect);
66     pictures.push_back(std::move(picture));
67   }
68   if (tiles.size() == 1)
69     return base::AdoptRef(new PictureSnapshot(std::move(pictures[0])));
70   SkPictureRecorder recorder;
71   SkCanvas* canvas = recorder.beginRecording(union_rect.Width(),
72                                              union_rect.Height(), nullptr, 0);
73   for (size_t i = 0; i < pictures.size(); ++i) {
74     canvas->save();
75     canvas->translate(tiles[i]->layer_offset.X() - union_rect.X(),
76                       tiles[i]->layer_offset.Y() - union_rect.Y());
77     pictures[i]->playback(canvas, nullptr);
78     canvas->restore();
79   }
80   return base::AdoptRef(
81       new PictureSnapshot(recorder.finishRecordingAsPicture()));
82 }
83 
IsEmpty() const84 bool PictureSnapshot::IsEmpty() const {
85   return picture_->cullRect().isEmpty();
86 }
87 
Replay(unsigned from_step,unsigned to_step,double scale) const88 Vector<uint8_t> PictureSnapshot::Replay(unsigned from_step,
89                                         unsigned to_step,
90                                         double scale) const {
91   const SkIRect bounds = picture_->cullRect().roundOut();
92   int width = ceil(scale * bounds.width());
93   int height = ceil(scale * bounds.height());
94 
95   // TODO(fmalita): convert this to SkSurface/SkImage, drop the intermediate
96   // SkBitmap.
97   SkBitmap bitmap;
98   bitmap.allocPixels(SkImageInfo::MakeN32Premul(width, height));
99   bitmap.eraseARGB(0, 0, 0, 0);
100   {
101     ReplayingCanvas canvas(bitmap, from_step, to_step);
102     // Disable LCD text preemptively, because the picture opacity is unknown.
103     // The canonical API involves SkSurface props, but since we're not
104     // SkSurface-based at this point (see TODO above) we (ab)use saveLayer for
105     // this purpose.
106     SkAutoCanvasRestore auto_restore(&canvas, false);
107     canvas.saveLayer(nullptr, nullptr);
108 
109     canvas.scale(scale, scale);
110     canvas.ResetStepCount();
111     picture_->playback(&canvas, &canvas);
112   }
113   Vector<uint8_t> encoded_image;
114 
115   SkPixmap src;
116   bool peekResult = bitmap.peekPixels(&src);
117   DCHECK(peekResult);
118 
119   SkPngEncoder::Options options;
120   options.fFilterFlags = SkPngEncoder::FilterFlag::kSub;
121   options.fZLibLevel = 3;
122   if (!ImageEncoder::Encode(&encoded_image, src, options))
123     return Vector<uint8_t>();
124 
125   return encoded_image;
126 }
127 
Profile(unsigned min_repeat_count,base::TimeDelta min_duration,const FloatRect * clip_rect) const128 Vector<Vector<base::TimeDelta>> PictureSnapshot::Profile(
129     unsigned min_repeat_count,
130     base::TimeDelta min_duration,
131     const FloatRect* clip_rect) const {
132   Vector<Vector<base::TimeDelta>> timings;
133   timings.ReserveInitialCapacity(min_repeat_count);
134   const SkIRect bounds = picture_->cullRect().roundOut();
135   SkBitmap bitmap;
136   bitmap.allocPixels(
137       SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()));
138   bitmap.eraseARGB(0, 0, 0, 0);
139 
140   base::TimeTicks now = base::TimeTicks::Now();
141   base::TimeTicks stop_time = now + min_duration;
142   for (unsigned step = 0; step < min_repeat_count || now < stop_time; ++step) {
143     Vector<base::TimeDelta> current_timings;
144     if (!timings.IsEmpty())
145       current_timings.ReserveInitialCapacity(timings.front().size());
146     ProfilingCanvas canvas(bitmap);
147     if (clip_rect) {
148       canvas.clipRect(SkRect::MakeXYWH(clip_rect->X(), clip_rect->Y(),
149                                        clip_rect->Width(),
150                                        clip_rect->Height()));
151       canvas.ResetStepCount();
152     }
153     canvas.SetTimings(&current_timings);
154     picture_->playback(&canvas);
155     timings.push_back(std::move(current_timings));
156     now = base::TimeTicks::Now();
157   }
158   return timings;
159 }
160 
SnapshotCommandLog() const161 std::unique_ptr<JSONArray> PictureSnapshot::SnapshotCommandLog() const {
162   LoggingCanvas canvas;
163   picture_->playback(&canvas);
164   return canvas.Log();
165 }
166 
167 }  // namespace blink
168