ImageFactory::ImageFactory( ResourceClient& resourceClient )
: mResourceClient(resourceClient),
- mMaxScale(0.5f),
+ mMaxScale( 4 / 1024.0f ), ///< Only allow a very tiny fudge factor in matching new requests to existing resource transactions: 4 pixels at a dimension of 1024, 2 at 512, ...
mReqIdCurrent(0)
{
}
{
ResourceTicketPtr ticket;
+ // See if any resource transaction has already been associated with this request:
const ResourceId resId = request.resourceId;
- if( resId == 0 )
+ if( resId != 0 )
{
- // Not yet associated with a ticketed async resource transaction, so attempt to
- // find a cached one and issue a new load if there isn't one in-flight:
-
+ // An IO operation has been started at some time for the request so recover the
+ // ticket that was created for that:
+ ticket = mResourceClient.RequestResourceTicket( resId ); ///@note Always succeeds in normal use.
+ }
+ else
+ {
+ // Request not yet associated with a ticketed async resource transaction, so
+ // attempt to find a compatible cached one:
const std::size_t urlHash = GetHashForCachedRequest( request );
ticket = FindCompatibleResource( request.url, urlHash, request.attributes );
-
- if( !ticket )
- {
- // Didn't find compatible resource already being loaded
- ticket = IssueLoadRequest( request.url, request.attributes );
- }
-
- request.resourceId = ticket->GetId();
}
- else
+
+ // Start a new resource IO transaction for the request if none is already happening:
+ if( !ticket )
{
- ticket = mResourceClient.RequestResourceTicket( resId );
- if( !ticket )
- {
- // Resource has been discarded since last load
- ticket = IssueLoadRequest( request.url, request.attributes );
- request.resourceId = ticket->GetId();
- }
- DALI_ASSERT_DEBUG( ticket->GetTypePath().type->id == ResourceBitmap ||
- ticket->GetTypePath().type->id == ResourceNativeImage ||
- ticket->GetTypePath().type->id == ResourceTargetImage );
+ ticket = IssueLoadRequest( request.url, request.attributes );
}
+ request.resourceId = ticket->GetId();
+ DALI_ASSERT_DEBUG( ticket->GetTypePath().type->id == ResourceBitmap ||
+ ticket->GetTypePath().type->id == ResourceNativeImage ||
+ ticket->GetTypePath().type->id == ResourceTargetImage );
return ticket;
}
bool ImageFactory::CompareAttributes( const Dali::ImageAttributes& requested,
const Dali::ImageAttributes& actual ) const
{
- // do not load image resource again if there is a similar resource loaded
- // eg. if size is less than 50% different of what we have
+ // do not load image resource again if there is a similar resource loaded:
// see explanation in image.h of what is deemed compatible
return (requested.GetScalingMode() == actual.GetScalingMode()) &&
+ (
+ (requested.GetFilterMode() == actual.GetFilterMode()) ||
+ (requested.GetFilterMode() == ImageAttributes::DontCare)
+ ) &&
(requested.GetPixelFormat() == actual.GetPixelFormat()) &&
(requested.GetFieldBorder() == actual.GetFieldBorder()) &&
(fabs(actual.GetFieldRadius() - requested.GetFieldRadius()) <= FLT_EPSILON) &&
(requested.IsDistanceField() == actual.IsDistanceField()) &&
- (fabsf(requested.GetWidth() - actual.GetWidth()) < actual.GetWidth() * mMaxScale) &&
- (fabsf(requested.GetHeight() - actual.GetHeight()) < actual.GetHeight() * mMaxScale);
+ (fabsf(requested.GetWidth() - actual.GetWidth()) <= actual.GetWidth() * mMaxScale) &&
+ (fabsf(requested.GetHeight() - actual.GetHeight()) <= actual.GetHeight() * mMaxScale);
}
Request* ImageFactory::InsertNewRequest( ResourceId resourceId, const std::string& filename, std::size_t urlHash, const ImageAttributes* attr )
// CLASS HEADER
#include <dali/public-api/images/image-attributes.h>
-// INTERNAL INCLUDES
-#include <dali/public-api/common/constants.h>
-#include <dali/public-api/common/dali-common.h>
-#include <dali/public-api/math/math-utils.h>
+// EXTERNAL INCLUDES
+#include <cmath>
namespace Dali
{
struct ImageAttributes::ImageAttributesImpl
{
ImageAttributesImpl()
- : width(0),
+ : fieldRadius(4.0f),
+ fieldBorder(4),
+ width(0),
height(0),
scaling(ShrinkToFit),
+ filtering(Box),
pixelformat(Pixel::RGBA8888),
mOrientationCorrection(false),
- isDistanceField(false),
- fieldRadius(4.0f),
- fieldBorder(4)
+ isDistanceField(false)
{
}
}
ImageAttributesImpl(const ImageAttributesImpl& rhs)
- : width( rhs.width ),
+ : fieldRadius( rhs.fieldRadius ),
+ fieldBorder( rhs.fieldBorder ),
+ width( rhs.width ),
height( rhs.height ),
scaling( rhs.scaling ),
+ filtering( rhs.filtering ),
pixelformat( rhs.pixelformat ),
mOrientationCorrection( rhs.mOrientationCorrection ),
- isDistanceField( rhs.isDistanceField ),
- fieldRadius( rhs.fieldRadius ),
- fieldBorder( rhs.fieldBorder )
+ isDistanceField( rhs.isDistanceField )
{
}
width = rhs.width;
height = rhs.height;
scaling = rhs.scaling;
+ filtering = rhs.filtering;
+
pixelformat = rhs.pixelformat;
mOrientationCorrection = rhs.mOrientationCorrection;
isDistanceField = rhs.isDistanceField;
return *this;
}
- unsigned int width; ///< image width in pixels
- unsigned int height; ///< image height in pixels
- ScalingMode scaling; ///< scaling option, ShrinkToFit is default
- Pixel::Format pixelformat; ///< pixel format, default is RGBA8888
-
- bool mOrientationCorrection; ///< If true, image pixels are reordered according to orientation metadata on load.
-
- // For distance fields :
- bool isDistanceField; ///< true, if the image is a distancefield. Default is false.
float fieldRadius; ///< The minimum search radius to check for differing pixels
- int fieldBorder; ///< The amount of distancefield cells to add around the data (for glow/shadow effects)
+ int fieldBorder : 16; ///< The amount of distancefield cells to add around the data (for glow/shadow effects)
+ unsigned int width : 16; ///< image width in pixels
+ unsigned int height : 16; ///< image height in pixels
+ ScalingMode scaling : 3; ///< scaling option, ShrinkToFit is default
+ FilterMode filtering : 3; ///< filtering option. Box is the default
+ Pixel::Format pixelformat : 5; ///< pixel format, default is RGBA8888
+ bool mOrientationCorrection : 1; ///< If true, image pixels are reordered according to orientation metadata on load.
+ bool isDistanceField : 1; ///< true, if the image is a distancefield. Default is false.
};
impl->scaling = scale;
}
+void ImageAttributes::SetFilterMode( FilterMode filtering )
+{
+ impl->filtering = filtering;
+}
+
void ImageAttributes::SetOrientationCorrection(const bool enabled)
{
impl->mOrientationCorrection = enabled;
return impl->scaling;
}
+ImageAttributes::FilterMode ImageAttributes::GetFilterMode() const
+{
+ return impl->filtering;
+}
+
bool ImageAttributes::IsDistanceField() const
{
return impl->isDistanceField;
return a.impl->scaling < b.impl->scaling;
}
+ if (a.impl->filtering != b.impl->filtering)
+ {
+ return a.impl->filtering < b.impl->filtering;
+ }
+
if (a.impl->isDistanceField && b.impl->isDistanceField)
{
if (fabs(a.impl->fieldRadius - b.impl->fieldRadius) > Math::MACHINE_EPSILON_0)
a.impl->mOrientationCorrection == b.impl->mOrientationCorrection &&
a.impl->pixelformat == b.impl->pixelformat &&
a.impl->scaling == b.impl->scaling &&
+ a.impl->filtering == b.impl->filtering &&
a.impl->isDistanceField == b.impl->isDistanceField &&
fabs(a.impl->fieldRadius - b.impl->fieldRadius) < Math::MACHINE_EPSILON_0 &&
a.impl->fieldBorder == b.impl->fieldBorder;
/**
* @brief Scaling options, used when resizing images on load to fit desired dimensions.
*
+ * A scaling mode controls the region of a loaded image to be mapped to the
+ * desired image rectangle specified using ImageAttributes.SetSize().
* All scaling modes preserve the aspect ratio of the image contents.
*/
enum ScalingMode
FitHeight ///< Image fills whole height. Width is scaled proportionately to maintain aspect ratio.
};
+ /**
+ * @brief Filtering options, used when resizing images on load to sample original pixels.
+ *
+ * A FilterMode controls how pixels in the raw image on-disk are sampled and
+ * combined to generate each pixel of the destination loaded image.
+ *
+ * @note NoFilter and Box modes do not guarantee that the loaded pixel array
+ * exactly matches the rectangle specified by the desired dimensions and
+ * ScalingMode, but all other filter modes do if the desired dimensions are
+ * `<=` the raw dimensions of the image file.
+ */
+ enum FilterMode
+ {
+ Box, ///< Iteratively box filter to generate an image of 1/2, 1/4, 1/8, ... width and height and
+ /// approximately the desired size, then if the ScaleToFill scaling mode is enabled, cut away the
+ /// top/bottom or left/right borders of the image to match the aspect ratio of desired dimensions.
+ /// This is the default.
+ Nearest, ///< For each output pixel, read one input pixel.
+ Linear, ///< For each output pixel, read a quad of four input pixels and write a weighted average of them.
+ BoxThenNearest, ///< Iteratively box filter to generate an image of 1/2, 1/4, 1/8, ... width and height and
+ /// approximately the desired size, then for each output pixel, read one pixel from the last level
+ /// of box filtering.
+ BoxThenLinear, ///< Iteratively box filter to almost the right size, then for each output pixel, read four pixels
+ /// from the last level of box filtering and write their weighted average.
+ NoFilter, ///< No filtering is performed. If the ScaleToFill scaling mode is enabled, the borders of the
+ /// image may be trimmed to match the aspect ratio of the desired dimensions.
+ DontCare ///< For when the client strongly prefers a cache-hit. Defaults to Box.
+ };
+
static const ImageAttributes DEFAULT_ATTRIBUTES; ///< Default attributes have no size
/**
void SetScalingMode(ScalingMode scalingMode);
/**
+ * @brief Setter for the FilterMode.
+ * By default, Box is set.
+ * @param [in] filterMode The desired filter mode.
+ */
+ void SetFilterMode( FilterMode filterMode );
+
+ /**
* @brief Set whether the image will be rotated/flipped back into portrait orientation.
*
* This will only be necessary if metadata indicates that the
ScalingMode GetScalingMode() const;
/**
+ * @brief Getter for the FilterMode
+ *
+ * @return The FilterMode previously set, or the default value if none has
+ * been.
+ */
+ FilterMode GetFilterMode() const;
+
+ /**
* @brief Return if the attribute set up as a distance field.
*
* @return true, if the attribute is a distance field.