Message ID | 20200729093154.12296-2-david.plowman@raspberrypi.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Hi David, Thank you for the patch. On Wed, Jul 29, 2020 at 10:31:50AM +0100, David Plowman wrote: > Add implementation of 2d plane transforms, and include a Transform > field in the CameraConfiguration to record the application's choice of > image transform for this session. > --- > include/libcamera/camera.h | 3 + > include/libcamera/meson.build | 1 + > include/libcamera/transform.h | 193 ++++++++++++++++++++++++++++++++++ > 3 files changed, 197 insertions(+) > create mode 100644 include/libcamera/transform.h > > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h > index 4d1a4a9..fd9355e 100644 > --- a/include/libcamera/camera.h > +++ b/include/libcamera/camera.h > @@ -16,6 +16,7 @@ > #include <libcamera/request.h> > #include <libcamera/signal.h> > #include <libcamera/stream.h> > +#include <libcamera/transform.h> > > namespace libcamera { > > @@ -60,6 +61,8 @@ public: > bool empty() const; > std::size_t size() const; > > + Transform userTransform; > + > protected: > CameraConfiguration(); > This needs to be documented in the CameraConfiguration class. Didn't doxygen warn you ? I would also split this to a separate patch, and update the existing pipeline handlers to always set userTransform to Identity in their validate() function. > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build > index cdb8e03..7fae5e5 100644 > --- a/include/libcamera/meson.build > +++ b/include/libcamera/meson.build > @@ -19,6 +19,7 @@ libcamera_public_headers = files([ > 'span.h', > 'stream.h', > 'timer.h', > + 'transform.h', > ]) > > include_dir = join_paths(libcamera_include_dir, 'libcamera') > diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h > new file mode 100644 > index 0000000..cfd5026 > --- /dev/null > +++ b/include/libcamera/transform.h > @@ -0,0 +1,193 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2020, Google Inc. As mentioned in the reply to the cover letter, this should have your copyright. > + * > + * transform.h - Implementation of 2d plane transforms '2d' or '2D' ? > + */ > + > +#ifndef __LIBCAMERA_TRANSFORM_H__ > +#define __LIBCAMERA_TRANSFORM_H__ > + > +#include <string> You should also include stdint.h for int32_t, although I think you could use int instead of int32_t. > + > +namespace libcamera { > + > +class Transform > +{ > +public: > + constexpr Transform() > + : Transform(Identity) > + { > + } > + > + constexpr static Transform identity() > + { > + return Transform(Identity); > + } > + > + constexpr static Transform rot0() > + { > + return identity(); > + } > + > + constexpr static Transform hflip() > + { > + return Transform(HFlip); > + } > + > + constexpr static Transform vflip() > + { > + return Transform(VFlip); > + } > + > + constexpr static Transform hvflip() > + { > + return Transform(HVFlip); > + } > + > + constexpr static Transform rot180() > + { > + return hvflip(); > + } > + > + constexpr static Transform transpose() > + { > + return Transform(Transpose); > + } > + > + constexpr static Transform rot270() > + { > + return Transform(Rot270); > + } > + > + constexpr static Transform rot90() > + { > + return Transform(Rot90); > + } > + > + constexpr static Transform rot180Transpose() > + { > + return Transform(Rot180Transpose); > + } > + > + constexpr static Transform hvflipTranspose() > + { > + return rot180Transpose(); > + } > + Wouldn't it be simpler to make the Type enum public, as well as the constructor that takes a Type ? I will also be way less code to type :-) > + constexpr static bool rotation(int32_t angle, Transform &xfm) > + { > + bool success = true; > + angle = angle % 360; > + if (angle < 0) > + angle += 360; > + > + if (angle == 0) > + xfm = identity(); > + else if (angle == 90) > + xfm = rot90(); > + else if (angle == 180) > + xfm = rot180(); > + else if (angle == 270) > + xfm = rot270(); > + else > + success = false; > + > + return success; > + } > + > + constexpr bool rotation(int32_t &angle) const > + { > + bool success = true; > + > + if (type_ == Identity) > + angle = 0; > + else if (type_ == Rot90) > + angle = 90; > + else if (type_ == HVFlip) > + angle = 180; > + else if (type_ == Rot270) > + angle = 270; > + else > + success = false; > + > + return success; > + } That's two "rotation" functions, one applying a rotation, the other one returning the rotation. I think it's a bit confusing. I would turn the former into bool rotate(int angle); and the later into constexpr unsigned int rotationAngle(bool *success = nullptr) const; I also think the two functions should be implemented in the .cpp file. > + > + constexpr bool contains(Transform xfm) const > + { > + return (type_ & xfm.type_) == xfm.type_; > + } This function is used in the rest of the series to check if a transformation contains hflip, vflip or transpose. For that use case it's fine, but it could also be used with Transform::rot180Transpose().contains(Transform::rot270()) and return true, which is quite confusing to read. I'm not sure how to improve that though. Maybe defining three functions, to test for hflip, vflip and transpose separately ? isHorizontallyFlipped(), isVerticallyFlipped, isTransposed ? Or isHFlipped(), isVFlipped() for the first two ? > + > + constexpr Transform inverse() const > + { > + /* They're all self-inverses, except for Rot90 and Rot270. */ > + const Type inverses[] = { Identity, HFlip, VFlip, HVFlip, > + Transpose, Rot90, Rot270, Rot180Transpose }; Shouldn't this be static const ? > + > + return Transform(inverses[type_]); > + } > + > + constexpr Transform operator*(Transform xfm) const > + { > + /* > + * Reorder the operations so that we imagine doing xfm's transpose > + * (if any) after our flips. The effect is to swap our hflips for > + * vflips and vice versa, after which we can just xor all the bits. > + */ > + int reorderedType = type_; > + if (xfm.type_ & Transpose) > + reorderedType = (type_ & 4) | ((type_ & 1) << 1) | ((type_ & 2) >> 1); > + > + return Transform((Type)(reorderedType ^ xfm.type_)); > + } I would move the implementation of these two functions to the .cpp file too. > + > + bool operator==(Transform xfm) const > + { > + return type_ == xfm.type_; > + } > + > + bool operator!=(Transform xfm) const > + { > + return !(*this == xfm); > + } > + > + std::string toString() const > + { > + char const *strings[] = { static const char ? > + "identity", > + "hflip", > + "vflip", > + "hvflip", > + "transpose", > + "rot270", > + "rot90", > + "rot180transpose" > + }; > + return strings[type_]; > + } Of course all this needs documentation :-) A good introduction (in the \class Transform block) would be very useful to explain the concepts I think. > + > +private: > + enum Type : int { > + Identity = 0, > + HFlip = 1, > + VFlip = 2, > + HVFlip = HFlip | VFlip, > + Transpose = 4, > + Rot270 = HFlip | Transpose, > + Rot90 = VFlip | Transpose, > + Rot180Transpose = HFlip | VFlip | Transpose > + }; > + > + constexpr Transform(Type type) > + : type_(type) > + { > + } > + > + Type type_; > +}; > + > +} /* namespace libcamera */ > + > +#endif /* __LIBCAMERA_TRANSFORM_H__ */ > + Extra blank line.
Hi Laurent Thanks for the feedback. That all looks good, let me just clarify one or two points. On Wed, 29 Jul 2020 at 16:12, Laurent Pinchart <laurent.pinchart@ideasonboard.com> wrote: > > Hi David, > > Thank you for the patch. > > On Wed, Jul 29, 2020 at 10:31:50AM +0100, David Plowman wrote: > > Add implementation of 2d plane transforms, and include a Transform > > field in the CameraConfiguration to record the application's choice of > > image transform for this session. > > --- > > include/libcamera/camera.h | 3 + > > include/libcamera/meson.build | 1 + > > include/libcamera/transform.h | 193 ++++++++++++++++++++++++++++++++++ > > 3 files changed, 197 insertions(+) > > create mode 100644 include/libcamera/transform.h > > > > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h > > index 4d1a4a9..fd9355e 100644 > > --- a/include/libcamera/camera.h > > +++ b/include/libcamera/camera.h > > @@ -16,6 +16,7 @@ > > #include <libcamera/request.h> > > #include <libcamera/signal.h> > > #include <libcamera/stream.h> > > +#include <libcamera/transform.h> > > > > namespace libcamera { > > > > @@ -60,6 +61,8 @@ public: > > bool empty() const; > > std::size_t size() const; > > > > + Transform userTransform; > > + > > protected: > > CameraConfiguration(); > > > > This needs to be documented in the CameraConfiguration class. Didn't > doxygen warn you ? I would also split this to a separate patch, and > update the existing pipeline handlers to always set userTransform to > Identity in their validate() function. I wasn't sure I'm qualified to touch existing pipeline handlers, but I guess I can do so if necessary! I'm still uncertain whether "adjusting" the transform is reasonable or whether failing is better. In the Raspberry Pi pipeline handler I felt that adjusting a transform to a completely different one isn't a "reasonable" change, and the application should know and not carry on with the thing it didn't want. It's not like changing an image size "a bit", after all. But I'm open to persuasion... > > > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build > > index cdb8e03..7fae5e5 100644 > > --- a/include/libcamera/meson.build > > +++ b/include/libcamera/meson.build > > @@ -19,6 +19,7 @@ libcamera_public_headers = files([ > > 'span.h', > > 'stream.h', > > 'timer.h', > > + 'transform.h', > > ]) > > > > include_dir = join_paths(libcamera_include_dir, 'libcamera') > > diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h > > new file mode 100644 > > index 0000000..cfd5026 > > --- /dev/null > > +++ b/include/libcamera/transform.h > > @@ -0,0 +1,193 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2020, Google Inc. > > As mentioned in the reply to the cover letter, this should have your > copyright. > > > + * > > + * transform.h - Implementation of 2d plane transforms > > '2d' or '2D' ? > > > + */ > > + > > +#ifndef __LIBCAMERA_TRANSFORM_H__ > > +#define __LIBCAMERA_TRANSFORM_H__ > > + > > +#include <string> > > You should also include stdint.h for int32_t, although I think you could > use int instead of int32_t. > > > + > > +namespace libcamera { > > + > > +class Transform > > +{ > > +public: > > + constexpr Transform() > > + : Transform(Identity) > > + { > > + } > > + > > + constexpr static Transform identity() > > + { > > + return Transform(Identity); > > + } > > + > > + constexpr static Transform rot0() > > + { > > + return identity(); > > + } > > + > > + constexpr static Transform hflip() > > + { > > + return Transform(HFlip); > > + } > > + > > + constexpr static Transform vflip() > > + { > > + return Transform(VFlip); > > + } > > + > > + constexpr static Transform hvflip() > > + { > > + return Transform(HVFlip); > > + } > > + > > + constexpr static Transform rot180() > > + { > > + return hvflip(); > > + } > > + > > + constexpr static Transform transpose() > > + { > > + return Transform(Transpose); > > + } > > + > > + constexpr static Transform rot270() > > + { > > + return Transform(Rot270); > > + } > > + > > + constexpr static Transform rot90() > > + { > > + return Transform(Rot90); > > + } > > + > > + constexpr static Transform rot180Transpose() > > + { > > + return Transform(Rot180Transpose); > > + } > > + > > + constexpr static Transform hvflipTranspose() > > + { > > + return rot180Transpose(); > > + } > > + > > Wouldn't it be simpler to make the Type enum public, as well as the > constructor that takes a Type ? I will also be way less code to type :-) I did wonder about this. I didn't like making the Type public because either you use enum class and end up writing stuff like Transform(Transform::Type::Identity) instead of Transform::identity(), or you could use a straight enum and write Transform(Transform::Identity) which I like, but you have to worry about what this means: Transform transform = Transform::Identity * Transform::HFlip; So I thought typing more in the header file was better than putting the burden on the client code. But I don't feel very strongly... > > > + constexpr static bool rotation(int32_t angle, Transform &xfm) > > + { > > + bool success = true; > > + angle = angle % 360; > > + if (angle < 0) > > + angle += 360; > > + > > + if (angle == 0) > > + xfm = identity(); > > + else if (angle == 90) > > + xfm = rot90(); > > + else if (angle == 180) > > + xfm = rot180(); > > + else if (angle == 270) > > + xfm = rot270(); > > + else > > + success = false; > > + > > + return success; > > + } > > + > > + constexpr bool rotation(int32_t &angle) const > > + { > > + bool success = true; > > + > > + if (type_ == Identity) > > + angle = 0; > > + else if (type_ == Rot90) > > + angle = 90; > > + else if (type_ == HVFlip) > > + angle = 180; > > + else if (type_ == Rot270) > > + angle = 270; > > + else > > + success = false; > > + > > + return success; > > + } > > That's two "rotation" functions, one applying a rotation, the other one > returning the rotation. I think it's a bit confusing. I would turn the > former into > > bool rotate(int angle); > > and the later into > > constexpr unsigned int rotationAngle(bool *success = nullptr) const; > > I also think the two functions should be implemented in the .cpp file. Yes, two functions with the same name was always nasty. The first one is actually creating a Transform from a rotation angle, not applying a rotation (so obviously documentation will have to be clear on that). If we prefer the success code as the optional parameter it could look like: constexpr static Transform rotation(int angle, bool *success = nullptr); For the second one I like rotationAngle. > > > + > > + constexpr bool contains(Transform xfm) const > > + { > > + return (type_ & xfm.type_) == xfm.type_; > > + } > > This function is used in the rest of the series to check if a > transformation contains hflip, vflip or transpose. For that use case > it's fine, but it could also be used with > > Transform::rot180Transpose().contains(Transform::rot270()) > > and return true, which is quite confusing to read. I'm not sure how to > improve that though. Maybe defining three functions, to test for hflip, > vflip and transpose separately ? isHorizontallyFlipped(), > isVerticallyFlipped, isTransposed ? Or isHFlipped(), isVFlipped() for > the first two ? Yep, I could go with isHFlipped(), isVFlipped(), isTransposed(). Thanks! David > > > + > > + constexpr Transform inverse() const > > + { > > + /* They're all self-inverses, except for Rot90 and Rot270. */ > > + const Type inverses[] = { Identity, HFlip, VFlip, HVFlip, > > + Transpose, Rot90, Rot270, Rot180Transpose }; > > Shouldn't this be static const ? > > > + > > + return Transform(inverses[type_]); > > + } > > + > > + constexpr Transform operator*(Transform xfm) const > > + { > > + /* > > + * Reorder the operations so that we imagine doing xfm's transpose > > + * (if any) after our flips. The effect is to swap our hflips for > > + * vflips and vice versa, after which we can just xor all the bits. > > + */ > > + int reorderedType = type_; > > + if (xfm.type_ & Transpose) > > + reorderedType = (type_ & 4) | ((type_ & 1) << 1) | ((type_ & 2) >> 1); > > + > > + return Transform((Type)(reorderedType ^ xfm.type_)); > > + } > > I would move the implementation of these two functions to the .cpp file > too. > > > + > > + bool operator==(Transform xfm) const > > + { > > + return type_ == xfm.type_; > > + } > > + > > + bool operator!=(Transform xfm) const > > + { > > + return !(*this == xfm); > > + } > > + > > + std::string toString() const > > + { > > + char const *strings[] = { > > static const char ? > > > + "identity", > > + "hflip", > > + "vflip", > > + "hvflip", > > + "transpose", > > + "rot270", > > + "rot90", > > + "rot180transpose" > > + }; > > + return strings[type_]; > > + } > > Of course all this needs documentation :-) A good introduction (in the > \class Transform block) would be very useful to explain the concepts I > think. > > > + > > +private: > > + enum Type : int { > > + Identity = 0, > > + HFlip = 1, > > + VFlip = 2, > > + HVFlip = HFlip | VFlip, > > + Transpose = 4, > > + Rot270 = HFlip | Transpose, > > + Rot90 = VFlip | Transpose, > > + Rot180Transpose = HFlip | VFlip | Transpose > > + }; > > + > > + constexpr Transform(Type type) > > + : type_(type) > > + { > > + } > > + > > + Type type_; > > +}; > > + > > +} /* namespace libcamera */ > > + > > +#endif /* __LIBCAMERA_TRANSFORM_H__ */ > > + > > Extra blank line. > > -- > Regards, > > Laurent Pinchart
Hi David, On Wed, Jul 29, 2020 at 05:36:16PM +0100, David Plowman wrote: > Hi Laurent > > Thanks for the feedback. That all looks good, let me just clarify one > or two points. > > On Wed, 29 Jul 2020 at 16:12, Laurent Pinchart wrote: > > On Wed, Jul 29, 2020 at 10:31:50AM +0100, David Plowman wrote: > > > Add implementation of 2d plane transforms, and include a Transform > > > field in the CameraConfiguration to record the application's choice of > > > image transform for this session. > > > --- > > > include/libcamera/camera.h | 3 + > > > include/libcamera/meson.build | 1 + > > > include/libcamera/transform.h | 193 ++++++++++++++++++++++++++++++++++ > > > 3 files changed, 197 insertions(+) > > > create mode 100644 include/libcamera/transform.h > > > > > > diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h > > > index 4d1a4a9..fd9355e 100644 > > > --- a/include/libcamera/camera.h > > > +++ b/include/libcamera/camera.h > > > @@ -16,6 +16,7 @@ > > > #include <libcamera/request.h> > > > #include <libcamera/signal.h> > > > #include <libcamera/stream.h> > > > +#include <libcamera/transform.h> > > > > > > namespace libcamera { > > > > > > @@ -60,6 +61,8 @@ public: > > > bool empty() const; > > > std::size_t size() const; > > > > > > + Transform userTransform; > > > + > > > protected: > > > CameraConfiguration(); > > > > > > > This needs to be documented in the CameraConfiguration class. Didn't > > doxygen warn you ? I would also split this to a separate patch, and > > update the existing pipeline handlers to always set userTransform to > > Identity in their validate() function. > > I wasn't sure I'm qualified to touch existing pipeline handlers, but I > guess I can do so if necessary! I'm still uncertain whether > "adjusting" the transform is reasonable or whether failing is better. > In the Raspberry Pi pipeline handler I felt that adjusting a transform > to a completely different one isn't a "reasonable" change, and the > application should know and not carry on with the thing it didn't > want. It's not like changing an image size "a bit", after all. But I'm > open to persuasion... In that case we would need a mechanism to report the supported transformations, otherwise an application wouldn't know what do to if validate() fails. And speaking of that, wouldn't such a mechanism make sense in any case ? > > > diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build > > > index cdb8e03..7fae5e5 100644 > > > --- a/include/libcamera/meson.build > > > +++ b/include/libcamera/meson.build > > > @@ -19,6 +19,7 @@ libcamera_public_headers = files([ > > > 'span.h', > > > 'stream.h', > > > 'timer.h', > > > + 'transform.h', > > > ]) > > > > > > include_dir = join_paths(libcamera_include_dir, 'libcamera') > > > diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h > > > new file mode 100644 > > > index 0000000..cfd5026 > > > --- /dev/null > > > +++ b/include/libcamera/transform.h > > > @@ -0,0 +1,193 @@ > > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > > +/* > > > + * Copyright (C) 2020, Google Inc. > > > > As mentioned in the reply to the cover letter, this should have your > > copyright. > > > > > + * > > > + * transform.h - Implementation of 2d plane transforms > > > > '2d' or '2D' ? > > > > > + */ > > > + > > > +#ifndef __LIBCAMERA_TRANSFORM_H__ > > > +#define __LIBCAMERA_TRANSFORM_H__ > > > + > > > +#include <string> > > > > You should also include stdint.h for int32_t, although I think you could > > use int instead of int32_t. > > > > > + > > > +namespace libcamera { > > > + > > > +class Transform > > > +{ > > > +public: > > > + constexpr Transform() > > > + : Transform(Identity) > > > + { > > > + } > > > + > > > + constexpr static Transform identity() > > > + { > > > + return Transform(Identity); > > > + } > > > + > > > + constexpr static Transform rot0() > > > + { > > > + return identity(); > > > + } > > > + > > > + constexpr static Transform hflip() > > > + { > > > + return Transform(HFlip); > > > + } > > > + > > > + constexpr static Transform vflip() > > > + { > > > + return Transform(VFlip); > > > + } > > > + > > > + constexpr static Transform hvflip() > > > + { > > > + return Transform(HVFlip); > > > + } > > > + > > > + constexpr static Transform rot180() > > > + { > > > + return hvflip(); > > > + } > > > + > > > + constexpr static Transform transpose() > > > + { > > > + return Transform(Transpose); > > > + } > > > + > > > + constexpr static Transform rot270() > > > + { > > > + return Transform(Rot270); > > > + } > > > + > > > + constexpr static Transform rot90() > > > + { > > > + return Transform(Rot90); > > > + } > > > + > > > + constexpr static Transform rot180Transpose() > > > + { > > > + return Transform(Rot180Transpose); > > > + } > > > + > > > + constexpr static Transform hvflipTranspose() > > > + { > > > + return rot180Transpose(); > > > + } > > > + > > > > Wouldn't it be simpler to make the Type enum public, as well as the > > constructor that takes a Type ? I will also be way less code to type :-) > > I did wonder about this. I didn't like making the Type public because > either you use enum class and end up writing stuff like > Transform(Transform::Type::Identity) instead of Transform::identity(), > or you could use a straight enum and write > Transform(Transform::Identity) which I like, but you have to worry > about what this means: > Transform transform = Transform::Identity * Transform::HFlip; > > So I thought typing more in the header file was better than putting > the burden on the client code. But I don't feel very strongly... Exposing the enum would avoid the nasty reinterpret_cast in the next patch. I think an enum class would make sense, and if we really want to avoid using Transform::Type::Identity, we could add static const Transform Identity = Transform(Transform::Type::Identity); (same for the other ones) to allow usage of Transform::Identity. It may be a bit overkill though. In any case, I think static const Transform Identity = Transform(Transform::Type::Identity); would be better than constexpr static Transform identity() { return Transform(Identity); } as there's no need for functions. Exposing the enum would also allow using it in a bitfield to expose the supported values if needed, for instance to report the supported values. See https://lists.libcamera.org/pipermail/libcamera-devel/2020-July/011279.html. > > > + constexpr static bool rotation(int32_t angle, Transform &xfm) > > > + { > > > + bool success = true; > > > + angle = angle % 360; > > > + if (angle < 0) > > > + angle += 360; > > > + > > > + if (angle == 0) > > > + xfm = identity(); > > > + else if (angle == 90) > > > + xfm = rot90(); > > > + else if (angle == 180) > > > + xfm = rot180(); > > > + else if (angle == 270) > > > + xfm = rot270(); > > > + else > > > + success = false; > > > + > > > + return success; > > > + } > > > + > > > + constexpr bool rotation(int32_t &angle) const > > > + { > > > + bool success = true; > > > + > > > + if (type_ == Identity) > > > + angle = 0; > > > + else if (type_ == Rot90) > > > + angle = 90; > > > + else if (type_ == HVFlip) > > > + angle = 180; > > > + else if (type_ == Rot270) > > > + angle = 270; > > > + else > > > + success = false; > > > + > > > + return success; > > > + } > > > > That's two "rotation" functions, one applying a rotation, the other one > > returning the rotation. I think it's a bit confusing. I would turn the > > former into > > > > bool rotate(int angle); > > > > and the later into > > > > constexpr unsigned int rotationAngle(bool *success = nullptr) const; > > > > I also think the two functions should be implemented in the .cpp file. > > Yes, two functions with the same name was always nasty. The first one > is actually creating a Transform from a rotation angle, not applying a > rotation (so obviously documentation will have to be clear on that). > > If we prefer the success code as the optional parameter it could look like: > constexpr static Transform rotation(int angle, bool *success = nullptr); Wouldn't it make sense to apply a rotation to an existing Transform ? That's what I envisioned with bool rotate(int angle); and one could do Transform t = Transform::Identity; t.rotate(90); to achieve the same result. Or if we want to support Transform t = Transform::Identity.rotate(90); then the function could be defined as Transform rotate(int angle, bool *success = nullptr) const; It may make sense to call it rotated() in that case ("rotate" modifies the instance by applying a rotation, "rotated" returns a new instance that results from applying the rotation). This is the naming scheme we went for in geometry.h, see Size::alignDownTo() vs. Size::alignedDownTo(). Both rotate() and rotated() could be provided if needed. > For the second one I like rotationAngle. > > > > + > > > + constexpr bool contains(Transform xfm) const > > > + { > > > + return (type_ & xfm.type_) == xfm.type_; > > > + } > > > > This function is used in the rest of the series to check if a > > transformation contains hflip, vflip or transpose. For that use case > > it's fine, but it could also be used with > > > > Transform::rot180Transpose().contains(Transform::rot270()) > > > > and return true, which is quite confusing to read. I'm not sure how to > > improve that though. Maybe defining three functions, to test for hflip, > > vflip and transpose separately ? isHorizontallyFlipped(), > > isVerticallyFlipped, isTransposed ? Or isHFlipped(), isVFlipped() for > > the first two ? > > Yep, I could go with isHFlipped(), isVFlipped(), isTransposed(). > > > > + > > > + constexpr Transform inverse() const > > > + { > > > + /* They're all self-inverses, except for Rot90 and Rot270. */ > > > + const Type inverses[] = { Identity, HFlip, VFlip, HVFlip, > > > + Transpose, Rot90, Rot270, Rot180Transpose }; > > > > Shouldn't this be static const ? > > > > > + > > > + return Transform(inverses[type_]); > > > + } > > > + > > > + constexpr Transform operator*(Transform xfm) const > > > + { > > > + /* > > > + * Reorder the operations so that we imagine doing xfm's transpose > > > + * (if any) after our flips. The effect is to swap our hflips for > > > + * vflips and vice versa, after which we can just xor all the bits. > > > + */ > > > + int reorderedType = type_; > > > + if (xfm.type_ & Transpose) > > > + reorderedType = (type_ & 4) | ((type_ & 1) << 1) | ((type_ & 2) >> 1); > > > + > > > + return Transform((Type)(reorderedType ^ xfm.type_)); > > > + } > > > > I would move the implementation of these two functions to the .cpp file > > too. > > > > > + > > > + bool operator==(Transform xfm) const > > > + { > > > + return type_ == xfm.type_; > > > + } > > > + > > > + bool operator!=(Transform xfm) const > > > + { > > > + return !(*this == xfm); > > > + } > > > + > > > + std::string toString() const > > > + { > > > + char const *strings[] = { > > > > static const char ? > > > > > + "identity", > > > + "hflip", > > > + "vflip", > > > + "hvflip", > > > + "transpose", > > > + "rot270", > > > + "rot90", > > > + "rot180transpose" > > > + }; > > > + return strings[type_]; > > > + } > > > > Of course all this needs documentation :-) A good introduction (in the > > \class Transform block) would be very useful to explain the concepts I > > think. > > > > > + > > > +private: > > > + enum Type : int { > > > + Identity = 0, > > > + HFlip = 1, > > > + VFlip = 2, > > > + HVFlip = HFlip | VFlip, > > > + Transpose = 4, > > > + Rot270 = HFlip | Transpose, > > > + Rot90 = VFlip | Transpose, > > > + Rot180Transpose = HFlip | VFlip | Transpose > > > + }; > > > + > > > + constexpr Transform(Type type) > > > + : type_(type) > > > + { > > > + } > > > + > > > + Type type_; > > > +}; > > > + > > > +} /* namespace libcamera */ > > > + > > > +#endif /* __LIBCAMERA_TRANSFORM_H__ */ > > > + > > > > Extra blank line.
diff --git a/include/libcamera/camera.h b/include/libcamera/camera.h index 4d1a4a9..fd9355e 100644 --- a/include/libcamera/camera.h +++ b/include/libcamera/camera.h @@ -16,6 +16,7 @@ #include <libcamera/request.h> #include <libcamera/signal.h> #include <libcamera/stream.h> +#include <libcamera/transform.h> namespace libcamera { @@ -60,6 +61,8 @@ public: bool empty() const; std::size_t size() const; + Transform userTransform; + protected: CameraConfiguration(); diff --git a/include/libcamera/meson.build b/include/libcamera/meson.build index cdb8e03..7fae5e5 100644 --- a/include/libcamera/meson.build +++ b/include/libcamera/meson.build @@ -19,6 +19,7 @@ libcamera_public_headers = files([ 'span.h', 'stream.h', 'timer.h', + 'transform.h', ]) include_dir = join_paths(libcamera_include_dir, 'libcamera') diff --git a/include/libcamera/transform.h b/include/libcamera/transform.h new file mode 100644 index 0000000..cfd5026 --- /dev/null +++ b/include/libcamera/transform.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Google Inc. + * + * transform.h - Implementation of 2d plane transforms + */ + +#ifndef __LIBCAMERA_TRANSFORM_H__ +#define __LIBCAMERA_TRANSFORM_H__ + +#include <string> + +namespace libcamera { + +class Transform +{ +public: + constexpr Transform() + : Transform(Identity) + { + } + + constexpr static Transform identity() + { + return Transform(Identity); + } + + constexpr static Transform rot0() + { + return identity(); + } + + constexpr static Transform hflip() + { + return Transform(HFlip); + } + + constexpr static Transform vflip() + { + return Transform(VFlip); + } + + constexpr static Transform hvflip() + { + return Transform(HVFlip); + } + + constexpr static Transform rot180() + { + return hvflip(); + } + + constexpr static Transform transpose() + { + return Transform(Transpose); + } + + constexpr static Transform rot270() + { + return Transform(Rot270); + } + + constexpr static Transform rot90() + { + return Transform(Rot90); + } + + constexpr static Transform rot180Transpose() + { + return Transform(Rot180Transpose); + } + + constexpr static Transform hvflipTranspose() + { + return rot180Transpose(); + } + + constexpr static bool rotation(int32_t angle, Transform &xfm) + { + bool success = true; + angle = angle % 360; + if (angle < 0) + angle += 360; + + if (angle == 0) + xfm = identity(); + else if (angle == 90) + xfm = rot90(); + else if (angle == 180) + xfm = rot180(); + else if (angle == 270) + xfm = rot270(); + else + success = false; + + return success; + } + + constexpr bool rotation(int32_t &angle) const + { + bool success = true; + + if (type_ == Identity) + angle = 0; + else if (type_ == Rot90) + angle = 90; + else if (type_ == HVFlip) + angle = 180; + else if (type_ == Rot270) + angle = 270; + else + success = false; + + return success; + } + + constexpr bool contains(Transform xfm) const + { + return (type_ & xfm.type_) == xfm.type_; + } + + constexpr Transform inverse() const + { + /* They're all self-inverses, except for Rot90 and Rot270. */ + const Type inverses[] = { Identity, HFlip, VFlip, HVFlip, + Transpose, Rot90, Rot270, Rot180Transpose }; + + return Transform(inverses[type_]); + } + + constexpr Transform operator*(Transform xfm) const + { + /* + * Reorder the operations so that we imagine doing xfm's transpose + * (if any) after our flips. The effect is to swap our hflips for + * vflips and vice versa, after which we can just xor all the bits. + */ + int reorderedType = type_; + if (xfm.type_ & Transpose) + reorderedType = (type_ & 4) | ((type_ & 1) << 1) | ((type_ & 2) >> 1); + + return Transform((Type)(reorderedType ^ xfm.type_)); + } + + bool operator==(Transform xfm) const + { + return type_ == xfm.type_; + } + + bool operator!=(Transform xfm) const + { + return !(*this == xfm); + } + + std::string toString() const + { + char const *strings[] = { + "identity", + "hflip", + "vflip", + "hvflip", + "transpose", + "rot270", + "rot90", + "rot180transpose" + }; + return strings[type_]; + } + +private: + enum Type : int { + Identity = 0, + HFlip = 1, + VFlip = 2, + HVFlip = HFlip | VFlip, + Transpose = 4, + Rot270 = HFlip | Transpose, + Rot90 = VFlip | Transpose, + Rot180Transpose = HFlip | VFlip | Transpose + }; + + constexpr Transform(Type type) + : type_(type) + { + } + + Type type_; +}; + +} /* namespace libcamera */ + +#endif /* __LIBCAMERA_TRANSFORM_H__ */ +