in dev/ColorPicker/ColorSpectrum.cpp [859:1118]
void ColorSpectrum::CreateBitmapsAndColorMap()
{
auto&& layoutRoot = m_layoutRoot.get();
auto&& sizingGrid = m_sizingGrid.get();
auto&& inputTarget = m_inputTarget.get();
auto&& spectrumRectangle = m_spectrumRectangle.get();
auto&& spectrumEllipse = m_spectrumEllipse.get();
auto&& spectrumOverlayRectangle = m_spectrumOverlayRectangle.get();
auto&& spectrumOverlayEllipse = m_spectrumOverlayEllipse.get();
if (!m_layoutRoot ||
!m_sizingGrid ||
!m_inputTarget ||
!m_spectrumRectangle ||
!m_spectrumEllipse ||
!m_spectrumOverlayRectangle ||
!m_spectrumOverlayEllipse ||
SharedHelpers::IsInDesignMode())
{
return;
}
const double minDimension = min(layoutRoot.ActualWidth(), layoutRoot.ActualHeight());
if (minDimension == 0)
{
return;
}
sizingGrid.Width(minDimension);
sizingGrid.Height(minDimension);
if (sizingGrid.Clip())
{
sizingGrid.Clip().Rect({ 0, 0, static_cast<float>(minDimension), static_cast<float>(minDimension) });
}
inputTarget.Width(minDimension);
inputTarget.Height(minDimension);
spectrumRectangle.Width(minDimension);
spectrumRectangle.Height(minDimension);
spectrumEllipse.Width(minDimension);
spectrumEllipse.Height(minDimension);
spectrumOverlayRectangle.Width(minDimension);
spectrumOverlayRectangle.Height(minDimension);
spectrumOverlayEllipse.Width(minDimension);
spectrumOverlayEllipse.Height(minDimension);
const winrt::float4 hsvColor = HsvColor();
const int minHue = MinHue();
int maxHue = MaxHue();
const int minSaturation = MinSaturation();
int maxSaturation = MaxSaturation();
const int minValue = MinValue();
int maxValue = MaxValue();
const winrt::ColorSpectrumShape shape = Shape();
const winrt::ColorSpectrumComponents components = Components();
// If min >= max, then by convention, min is the only number that a property can have.
if (minHue >= maxHue)
{
maxHue = minHue;
}
if (minSaturation >= maxSaturation)
{
maxSaturation = minSaturation;
}
if (minValue >= maxValue)
{
maxValue = minValue;
}
const Hsv hsv = { hsv::GetHue(hsvColor), hsv::GetSaturation(hsvColor), hsv::GetValue(hsvColor) };
// The middle 4 are only needed and used in the case of hue as the third dimension.
// Saturation and luminosity need only a min and max.
shared_ptr<vector<::byte>> bgraMinPixelData = make_shared<vector<::byte>>();
shared_ptr<vector<::byte>> bgraMiddle1PixelData = make_shared<vector<::byte>>();
shared_ptr<vector<::byte>> bgraMiddle2PixelData = make_shared<vector<::byte>>();
shared_ptr<vector<::byte>> bgraMiddle3PixelData = make_shared<vector<::byte>>();
shared_ptr<vector<::byte>> bgraMiddle4PixelData = make_shared<vector<::byte>>();
shared_ptr<vector<::byte>> bgraMaxPixelData = make_shared<vector<::byte>>();
shared_ptr<vector<Hsv>> newHsvValues = make_shared<vector<Hsv>>();
const auto pixelCount = static_cast<size_t>(round(minDimension) * round(minDimension));
const size_t pixelDataSize = pixelCount * 4;
bgraMinPixelData->reserve(pixelDataSize);
// We'll only save pixel data for the middle bitmaps if our third dimension is hue.
if (components == winrt::ColorSpectrumComponents::ValueSaturation ||
components == winrt::ColorSpectrumComponents::SaturationValue)
{
bgraMiddle1PixelData->reserve(pixelDataSize);
bgraMiddle2PixelData->reserve(pixelDataSize);
bgraMiddle3PixelData->reserve(pixelDataSize);
bgraMiddle4PixelData->reserve(pixelDataSize);
}
bgraMaxPixelData->reserve(pixelDataSize);
newHsvValues->reserve(pixelCount);
const int minDimensionInt = static_cast<int>(round(minDimension));
winrt::WorkItemHandler workItemHandler(
[minDimensionInt, hsv, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue, shape, components,
bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues]
(winrt::IAsyncAction workItem)
{
// As the user perceives it, every time the third dimension not represented in the ColorSpectrum changes,
// the ColorSpectrum will visually change to accommodate that value. For example, if the ColorSpectrum handles hue and luminosity,
// and the saturation externally goes from 1.0 to 0.5, then the ColorSpectrum will visually change to look more washed out
// to represent that third dimension's new value.
// Internally, however, we don't want to regenerate the ColorSpectrum bitmap every single time this happens, since that's very expensive.
// In order to make it so that we don't have to, we implement an optimization where, rather than having only one bitmap,
// we instead have multiple that we blend together using opacity to create the effect that we want.
// In the case where the third dimension is saturation or luminosity, we only need two: one bitmap at the minimum value
// of the third dimension, and one bitmap at the maximum. Then we set the second's opacity at whatever the value of
// the third dimension is - e.g., a saturation of 0.5 implies an opacity of 50%.
// In the case where the third dimension is hue, we need six: one bitmap corresponding to red, yellow, green, cyan, blue, and purple.
// We'll then blend between whichever colors our hue exists between - e.g., an orange color would use red and yellow with an opacity of 50%.
// This optimization does incur slightly more startup time initially since we have to generate multiple bitmaps at once instead of only one,
// but the running time savings after that are *huge* when we can just set an opacity instead of generating a brand new bitmap.
if (shape == winrt::ColorSpectrumShape::Box)
{
for (int x = minDimensionInt - 1; x >= 0; --x)
{
for (int y = minDimensionInt - 1; y >= 0; --y)
{
if (workItem.Status() == winrt::AsyncStatus::Canceled)
{
break;
}
ColorSpectrum::FillPixelForBox(
x, y, hsv, minDimensionInt, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue,
bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData,
newHsvValues);
}
}
}
else
{
for (int y = 0; y < minDimensionInt; ++y)
{
for (int x = 0; x < minDimensionInt; ++x)
{
if (workItem.Status() == winrt::AsyncStatus::Canceled)
{
break;
}
ColorSpectrum::FillPixelForRing(
x, y, minDimensionInt / 2.0, hsv, components, minHue, maxHue, minSaturation, maxSaturation, minValue, maxValue,
bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData,
newHsvValues);
}
}
}
});
if (m_createImageBitmapAction)
{
m_createImageBitmapAction.Cancel();
}
m_createImageBitmapAction = winrt::ThreadPool::RunAsync(workItemHandler);
auto strongThis = get_strong();
m_createImageBitmapAction.Completed(winrt::AsyncActionCompletedHandler(
[strongThis, minDimension, components, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues]
(winrt::IAsyncAction asyncInfo, winrt::AsyncStatus asyncStatus)
{
if (asyncStatus != winrt::AsyncStatus::Completed)
{
return;
}
strongThis->m_createImageBitmapAction = nullptr;
strongThis->m_dispatcherHelper.RunAsync(
[strongThis, minDimension, bgraMinPixelData, bgraMiddle1PixelData, bgraMiddle2PixelData, bgraMiddle3PixelData, bgraMiddle4PixelData, bgraMaxPixelData, newHsvValues]()
{
const int pixelWidth = static_cast<int>(round(minDimension));
const int pixelHeight = static_cast<int>(round(minDimension));
const winrt::ColorSpectrumComponents components = strongThis->Components();
if (SharedHelpers::IsRS2OrHigher())
{
winrt::LoadedImageSurface minSurface = CreateSurfaceFromPixelData(pixelWidth, pixelHeight, bgraMinPixelData);
winrt::LoadedImageSurface maxSurface = CreateSurfaceFromPixelData(pixelWidth, pixelHeight, bgraMaxPixelData);
switch (components)
{
case winrt::ColorSpectrumComponents::HueValue:
case winrt::ColorSpectrumComponents::ValueHue:
strongThis->m_saturationMinimumSurface = minSurface;
strongThis->m_saturationMaximumSurface = maxSurface;
break;
case winrt::ColorSpectrumComponents::HueSaturation:
case winrt::ColorSpectrumComponents::SaturationHue:
strongThis->m_valueSurface = maxSurface;
break;
case winrt::ColorSpectrumComponents::ValueSaturation:
case winrt::ColorSpectrumComponents::SaturationValue:
strongThis->m_hueRedSurface = minSurface;
strongThis->m_hueYellowSurface = CreateSurfaceFromPixelData(pixelWidth, pixelHeight, bgraMiddle1PixelData);
strongThis->m_hueGreenSurface = CreateSurfaceFromPixelData(pixelWidth, pixelHeight, bgraMiddle2PixelData);
strongThis->m_hueCyanSurface = CreateSurfaceFromPixelData(pixelWidth, pixelHeight, bgraMiddle3PixelData);
strongThis->m_hueBlueSurface = CreateSurfaceFromPixelData(pixelWidth, pixelHeight, bgraMiddle4PixelData);
strongThis->m_huePurpleSurface = maxSurface;
break;
}
}
else
{
winrt::WriteableBitmap minBitmap = CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMinPixelData);
winrt::WriteableBitmap maxBitmap = CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMaxPixelData);
switch (components)
{
case winrt::ColorSpectrumComponents::HueValue:
case winrt::ColorSpectrumComponents::ValueHue:
strongThis->m_saturationMinimumBitmap = minBitmap;
strongThis->m_saturationMaximumBitmap = maxBitmap;
break;
case winrt::ColorSpectrumComponents::HueSaturation:
case winrt::ColorSpectrumComponents::SaturationHue:
strongThis->m_valueBitmap = maxBitmap;
break;
case winrt::ColorSpectrumComponents::ValueSaturation:
case winrt::ColorSpectrumComponents::SaturationValue:
strongThis->m_hueRedBitmap = minBitmap;
strongThis->m_hueYellowBitmap = CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle1PixelData);
strongThis->m_hueGreenBitmap = CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle2PixelData);
strongThis->m_hueCyanBitmap = CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle3PixelData);
strongThis->m_hueBlueBitmap = CreateBitmapFromPixelData(pixelWidth, pixelHeight, bgraMiddle4PixelData);
strongThis->m_huePurpleBitmap = maxBitmap;
break;
}
}
strongThis->m_shapeFromLastBitmapCreation = strongThis->Shape();
strongThis->m_componentsFromLastBitmapCreation = strongThis->Components();
strongThis->m_imageWidthFromLastBitmapCreation = minDimension;
strongThis->m_imageHeightFromLastBitmapCreation = minDimension;
strongThis->m_minHueFromLastBitmapCreation = strongThis->MinHue();
strongThis->m_maxHueFromLastBitmapCreation = strongThis->MaxHue();
strongThis->m_minSaturationFromLastBitmapCreation = strongThis->MinSaturation();
strongThis->m_maxSaturationFromLastBitmapCreation = strongThis->MaxSaturation();
strongThis->m_minValueFromLastBitmapCreation = strongThis->MinValue();
strongThis->m_maxValueFromLastBitmapCreation = strongThis->MaxValue();
strongThis->m_hsvValues = *newHsvValues;
strongThis->UpdateBitmapSources();
strongThis->UpdateEllipse();
});
}));
}