1 // Copyright 2019 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 #ifndef CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
6 #define CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
7 
8 #include <utility>
9 #include <vector>
10 
11 #include "base/callback.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/observer_list.h"
15 #include "base/optional.h"
16 #include "base/sequence_checker.h"
17 #include "ui/gfx/image/image_skia.h"
18 
19 namespace base {
20 class TimeTicks;
21 }  // namespace base
22 
23 // Stores compressed thumbnail data for a tab and can vend that data as an
24 // uncompressed image to observers.
25 class ThumbnailImage : public base::RefCounted<ThumbnailImage> {
26  public:
27   // Smart pointer to reference-counted compressed image data; in this case
28   // JPEG format.
29   using CompressedThumbnailData =
30       scoped_refptr<base::RefCountedData<std::vector<uint8_t>>>;
31 
32   // Observes uncompressed and/or compressed versions of the thumbnail image as
33   // they are available.
34   class Observer : public base::CheckedObserver {
35    public:
36     // Receives uncompressed thumbnail image data. Default is no-op.
37     virtual void OnThumbnailImageAvailable(gfx::ImageSkia thumbnail_image);
38 
39     // Receives compressed thumbnail image data. Default is no-op.
40     virtual void OnCompressedThumbnailDataAvailable(
41         CompressedThumbnailData thumbnail_data);
42 
43     // Provides a desired aspect ratio and minimum size that the observer will
44     // accept. If not specified, or if available thumbnail data is smaller in
45     // either dimension than this value, OnThumbnailImageAvailable will be
46     // called with an uncropped image. If this value is specified, and the
47     // available image is larger, the image passed to OnThumbnailImageAvailable
48     // will be cropped to the same aspect ratio (but otherwise unchanged,
49     // including scale).
50     //
51     // OnCompressedThumbnailDataAvailable is not affected by this value.
52     //
53     // This method is used to ensure that except for very small thumbnails, the
54     // image passed to OnThumbnailImageAvailable fits the needs of the observer
55     // for display purposes, without the observer having to further crop the
56     // image. The default is unspecified.
57     virtual base::Optional<gfx::Size> GetThumbnailSizeHint() const;
58   };
59 
60   // Represents the endpoint
61   class Delegate {
62    public:
63     // Called whenever the thumbnail starts or stops being observed.
64     // Because updating the thumbnail could be an expensive operation, it's
65     // useful to track when there are no observers. Default behavior is no-op.
66     virtual void ThumbnailImageBeingObservedChanged(bool is_being_observed) = 0;
67 
68    protected:
69     virtual ~Delegate();
70 
71    private:
72     friend class ThumbnailImage;
73     ThumbnailImage* thumbnail_ = nullptr;
74   };
75 
76   explicit ThumbnailImage(Delegate* delegate);
77 
has_data()78   bool has_data() const { return data_.get(); }
79 
80   void AddObserver(Observer* observer);
81   void RemoveObserver(Observer* observer);
82   bool HasObserver(const Observer* observer) const;
83 
84   // Sets the SkBitmap data and notifies observers with the resulting image.
85   void AssignSkBitmap(SkBitmap bitmap);
86 
87   // Requests that a thumbnail image be made available to observers. Does not
88   // guarantee that Observer::OnThumbnailImageAvailable() will be called, or how
89   // long it will take, though in most cases it should happen very quickly.
90   void RequestThumbnailImage();
91 
92   // Similar to RequestThumbnailImage() but requests only the compressed JPEG
93   // data. Users should listen for a call to
94   // Observer::OnCompressedThumbnailDataAvailable().
95   void RequestCompressedThumbnailData();
96 
97   // Returns the size of the compressed data backing this thumbnail.
98   // This size can be 0. Additionally, since this data is refcounted,
99   // it's possible this returns 0 even if the data is still allocated. A
100   // client can hold a reference to it after |this| drops its reference.
101   size_t GetCompressedDataSizeInBytes() const;
102 
set_async_operation_finished_callback_for_testing(base::RepeatingClosure callback)103   void set_async_operation_finished_callback_for_testing(
104       base::RepeatingClosure callback) {
105     async_operation_finished_callback_ = std::move(callback);
106   }
107 
108  private:
109   friend class Delegate;
110   friend class ThumbnailImageTest;
111   friend class base::RefCounted<ThumbnailImage>;
112 
113   virtual ~ThumbnailImage();
114 
115   void AssignJPEGData(base::TimeTicks assign_sk_bitmap_time,
116                       std::vector<uint8_t> data);
117   bool ConvertJPEGDataToImageSkiaAndNotifyObservers();
118   void NotifyUncompressedDataObservers(gfx::ImageSkia image);
119   void NotifyCompressedDataObservers(CompressedThumbnailData data);
120 
121   static std::vector<uint8_t> CompressBitmap(SkBitmap bitmap);
122   static gfx::ImageSkia UncompressImage(CompressedThumbnailData compressed);
123 
124   // Crops and returns a preview from a thumbnail of an entire web page. Uses
125   // logic appropriate for fixed-aspect previews (e.g. hover cards).
126   static gfx::ImageSkia CropPreviewImage(const gfx::ImageSkia& source_image,
127                                          const gfx::Size& minimum_size);
128 
129   Delegate* delegate_;
130 
131   CompressedThumbnailData data_;
132 
133   base::ObserverList<Observer> observers_;
134 
135   // Called when an asynchronous operation (such as encoding image data upon
136   // assignment or decoding image data for observers) finishes or fails.
137   // Intended for unit tests that want to wait for internal operations following
138   // AssignSkBitmap() or RequestThumbnailImage() calls.
139   base::RepeatingClosure async_operation_finished_callback_;
140 
141   SEQUENCE_CHECKER(sequence_checker_);
142 
143   base::WeakPtrFactory<ThumbnailImage> weak_ptr_factory_{this};
144 
145   DISALLOW_COPY_AND_ASSIGN(ThumbnailImage);
146 };
147 
148 #endif  // CHROME_BROWSER_UI_THUMBNAILS_THUMBNAIL_IMAGE_H_
149