summaryrefslogtreecommitdiffstats
path: root/image/Downscaler.h
blob: 6ce2d833bbcfd071bdaa7d81b9e793617542cb96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/**
 * Downscaler is a high-quality, streaming image downscaler based upon Skia's
 * scaling implementation.
 */

#ifndef mozilla_image_Downscaler_h
#define mozilla_image_Downscaler_h

#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "gfxPoint.h"
#include "nsRect.h"
#ifdef MOZ_ENABLE_SKIA
#  include "mozilla/gfx/ConvolutionFilter.h"
#endif

namespace mozilla {
namespace image {

/**
 * DownscalerInvalidRect wraps two invalidation rects: one in terms of the
 * original image size, and one in terms of the target size.
 */
struct DownscalerInvalidRect {
  nsIntRect mOriginalSizeRect;
  nsIntRect mTargetSizeRect;
};

#ifdef MOZ_ENABLE_SKIA

/**
 * Downscaler is a high-quality, streaming image downscaler based upon Skia's
 * scaling implementation.
 *
 * Decoders can construct a Downscaler once they know their target size, then
 * call BeginFrame() for each frame they decode. They should write a decoded row
 * into the buffer returned by RowBuffer(), and then call CommitRow() to signal
 * that they have finished.
 *

 * Because invalidations need to be computed in terms of the scaled version of
 * the image, Downscaler also tracks them. Decoders can call HasInvalidation()
 * and TakeInvalidRect() instead of tracking invalidations themselves.
 */
class Downscaler {
 public:
  /// Constructs a new Downscaler which to scale to size @aTargetSize.
  explicit Downscaler(const nsIntSize& aTargetSize);
  ~Downscaler();

  const nsIntSize& OriginalSize() const { return mOriginalSize; }
  const nsIntSize& TargetSize() const { return mTargetSize; }
  const nsIntSize FrameSize() const {
    return nsIntSize(mFrameRect.Width(), mFrameRect.Height());
  }
  const gfxSize& Scale() const { return mScale; }

  /**
   * Begins a new frame and reinitializes the Downscaler.
   *
   * @param aOriginalSize The original size of this frame, before scaling.
   * @param aFrameRect The region of  the original image which has data.
   *                   Every pixel outside @aFrameRect is considered blank and
   *                   has zero alpha.
   * @param aOutputBuffer The buffer to which the Downscaler should write its
   *                      output; this is the same buffer where the Decoder
   *                      would write its output when not downscaling during
   *                      decode.
   * @param aHasAlpha Whether or not this frame has an alpha channel.
   *                  Performance is a little better if it doesn't have one.
   * @param aFlipVertically If true, output rows will be written to the output
   *                        buffer in reverse order vertically, which matches
   *                        the way they are stored in some image formats.
   */
  nsresult BeginFrame(const nsIntSize& aOriginalSize,
                      const Maybe<nsIntRect>& aFrameRect,
                      uint8_t* aOutputBuffer, bool aHasAlpha,
                      bool aFlipVertically = false);

  bool IsFrameComplete() const {
    return mCurrentInLine >= mOriginalSize.height;
  }

  /// Retrieves the buffer into which the Decoder should write each row.
  uint8_t* RowBuffer() {
    return mRowBuffer.get() + mFrameRect.X() * sizeof(uint32_t);
  }

  /// Clears the current row buffer.
  void ClearRow() { ClearRestOfRow(0); }

  /// Clears the current row buffer starting at @aStartingAtCol.
  void ClearRestOfRow(uint32_t aStartingAtCol);

  /// Signals that the decoder has finished writing a row into the row buffer.
  void CommitRow();

  /// Returns true if there is a non-empty invalid rect available.
  bool HasInvalidation() const;

  /// Takes the Downscaler's current invalid rect and resets it.
  DownscalerInvalidRect TakeInvalidRect();

  /**
   * Resets the Downscaler's position in the image, for a new progressive pass
   * over the same frame. Because the same data structures can be reused, this
   * is more efficient than calling BeginFrame.
   */
  void ResetForNextProgressivePass();

 private:
  void DownscaleInputLine();
  void ReleaseWindow();
  void SkipToRow(int32_t aRow);

  nsIntSize mOriginalSize;
  nsIntSize mTargetSize;
  nsIntRect mFrameRect;
  gfxSize mScale;

  uint8_t* mOutputBuffer;

  UniquePtr<uint8_t[]> mRowBuffer;
  UniquePtr<uint8_t*[]> mWindow;

  gfx::ConvolutionFilter mXFilter;
  gfx::ConvolutionFilter mYFilter;

  int32_t mWindowCapacity;

  int32_t mLinesInBuffer;
  int32_t mPrevInvalidatedLine;
  int32_t mCurrentOutLine;
  int32_t mCurrentInLine;

  bool mHasAlpha : 1;
  bool mFlipVertically : 1;
};

#else

/**
 * Downscaler requires Skia to work, so we provide a dummy implementation if
 * Skia is disabled that asserts if constructed.
 */

class Downscaler {
 public:
  explicit Downscaler(const nsIntSize&) : mScale(1.0, 1.0) {
    MOZ_RELEASE_ASSERT(false, "Skia is not enabled");
  }

  const nsIntSize& OriginalSize() const { return mSize; }
  const nsIntSize& TargetSize() const { return mSize; }
  const gfxSize& Scale() const { return mScale; }

  nsresult BeginFrame(const nsIntSize&, const Maybe<nsIntRect>&, uint8_t*, bool,
                      bool = false) {
    return NS_ERROR_FAILURE;
  }

  bool IsFrameComplete() const { return false; }
  uint8_t* RowBuffer() { return nullptr; }
  void ClearRow() {}
  void ClearRestOfRow(uint32_t) {}
  void CommitRow() {}
  bool HasInvalidation() const { return false; }
  DownscalerInvalidRect TakeInvalidRect() { return DownscalerInvalidRect(); }
  void ResetForNextProgressivePass() {}
  const nsIntSize FrameSize() const { return nsIntSize(0, 0); }

 private:
  nsIntSize mSize;
  gfxSize mScale;
};

#endif  // MOZ_ENABLE_SKIA

}  // namespace image
}  // namespace mozilla

#endif  // mozilla_image_Downscaler_h