Skip to content

Commit

Permalink
ImageBufferHaiku: adapt to new backends system
Browse files Browse the repository at this point in the history
Introduced in aa9a420, this new system
abstracts away backend-specific details from platform-specific
implementations, allowing code reuse between platforms.

This commit adapts Haiku's ImageBufferBackend implementation to use this
new system.

A new method `backendAlphaPremultiplication()` is added for dealing with
BBitmap's sole support for unpremultiplied alpha. Interactions against
83cc46b remains to be seen as tests
requires WebGL2.

Testing with WebPositive shows more or less the same pass rate for
web-platform-tests/html/canvas/element/imagebitmap against the old code path.
No further tests has been done as DumpRenderTree couldn't run due to
a relocation error, making automated testing difficult.
  • Loading branch information
alaviss committed Aug 27, 2020
1 parent 461de5e commit 4c12618
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 234 deletions.
4 changes: 2 additions & 2 deletions Source/WebCore/platform/graphics/ImageBufferBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Vector<uint8_t> ImageBufferBackend::toBGRAData(void* data) const
uint8_t* srcRows = reinterpret_cast<uint8_t*>(data);

copyImagePixels(
AlphaPremultiplication::Premultiplied, backendColorFormat(), srcBytesPerRow, srcRows,
backendAlphaPremultiplication(), backendColorFormat(), srcBytesPerRow, srcRows,
AlphaPremultiplication::Unpremultiplied, ColorFormat::BGRA, destBytesPerRow, result.data(), m_logicalSize);

return result;
Expand Down Expand Up @@ -252,7 +252,7 @@ RefPtr<ImageData> ImageBufferBackend::getImageData(AlphaPremultiplication output
uint8_t* srcRows = reinterpret_cast<uint8_t*>(data) + srcRectClipped.y() * srcBytesPerRow + srcRectClipped.x() * 4;

copyImagePixels(
AlphaPremultiplication::Premultiplied, backendColorFormat(), srcBytesPerRow, srcRows,
backendAlphaPremultiplication(), backendColorFormat(), srcBytesPerRow, srcRows,
outputFormat, ColorFormat::RGBA, destBytesPerRow, destRows, destRect.size());

return imageData;
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/platform/graphics/ImageBufferBackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class ImageBufferBackend {

virtual unsigned bytesPerRow() const { return 4 * m_backendSize.width(); }
virtual ColorFormat backendColorFormat() const { return ColorFormat::RGBA; }
virtual AlphaPremultiplication backendAlphaPremultiplication() const { return AlphaPremultiplication::Premultiplied; }

template<typename T>
T toBackendCoordinates(T t) const
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/platform/graphics/haiku/ImageBufferDataHaiku.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ class IntSize;

class ImageBufferData {
public:
ImageBufferData(const FloatSize&);
ImageBufferData(const IntSize&);
~ImageBufferData();

NativeImagePtr m_bitmap;
BView* m_view;
GraphicsContext* m_context;

RefPtr<Image> m_image;
};

Expand Down
252 changes: 38 additions & 214 deletions Source/WebCore/platform/graphics/haiku/ImageBufferHaiku.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "config.h"
Expand Down Expand Up @@ -48,8 +48,8 @@
namespace WebCore {


ImageBufferData::ImageBufferData(const FloatSize& size)
: m_bitmap(adoptRef(new BitmapRef(BRect(0, 0, size.width() - 1., size.height() - 1.), B_RGBA32, true)))
ImageBufferData::ImageBufferData(const IntSize& size)
: m_bitmap(adoptRef(new BitmapRef(BRect(0, 0, size.width() - 1, size.height() - 1), B_RGBA32, true)))
, m_view(NULL)
, m_context(NULL)
{
Expand Down Expand Up @@ -92,45 +92,57 @@ ImageBufferData::~ImageBufferData()

NativeImagePtr ImageBufferHaikuSurfaceBackend::copyNativeImage(WebCore::BackingStoreCopy doCopy) const
{
if (doCopy == DontCopyBackingStore)
return m_data.m_bitmap;
else
return new BitmapRef(m_data.m_bitmap.get());
if (doCopy == DontCopyBackingStore)
return m_data.m_bitmap;
else
return new BitmapRef(m_data.m_bitmap.get());
}


std::unique_ptr<ImageBufferHaikuSurfaceBackend>
ImageBufferHaikuSurfaceBackend::create(const FloatSize& size, float resolutionScale,
ColorSpace colorSpace, const HostWindow* window)
ColorSpace colorSpace, const HostWindow* window)
{
IntSize backendSize = calculateBackendSize(size, resolutionScale);
if (backendSize.isEmpty())
return nullptr;

return std::unique_ptr<ImageBufferHaikuSurfaceBackend>(
new ImageBufferHaikuSurfaceBackend(size, resolutionScale, colorSpace, window));
new ImageBufferHaikuSurfaceBackend(size, backendSize, resolutionScale,
colorSpace, window));
}


std::unique_ptr<ImageBufferHaikuSurfaceBackend>
ImageBufferHaikuSurfaceBackend::create(WebCore::FloatSize const& size,
WebCore::GraphicsContext const&)
ImageBufferHaikuSurfaceBackend::create(const FloatSize& size,
const GraphicsContext&)
{
return std::unique_ptr<ImageBufferHaikuSurfaceBackend>(
new ImageBufferHaikuSurfaceBackend(size, 1, ColorSpace::SRGB, NULL));
return create(size, 1, ColorSpace::SRGB, NULL);
}


ImageBufferHaikuSurfaceBackend::ImageBufferHaikuSurfaceBackend(const FloatSize& size,
float resolutionScale, ColorSpace colorSpace, const HostWindow*)
: ImageBufferBackend(size, IntSize(size), resolutionScale, colorSpace)
, m_data(size)
, m_size(size)
, m_logicalSize(size)
, m_resolutionScale(resolutionScale)
ImageBufferHaikuSurfaceBackend::ImageBufferHaikuSurfaceBackend(
const FloatSize& logicalSize, const IntSize& backendSize,
float resolutionScale, ColorSpace colorSpace, const HostWindow*)
: ImageBufferBackend(logicalSize, backendSize, resolutionScale, colorSpace)
, m_data(backendSize)
{
}

ImageBufferHaikuSurfaceBackend::~ImageBufferHaikuSurfaceBackend()
{
}

ColorFormat ImageBufferHaikuSurfaceBackend::backendColorFormat() const
{
return ColorFormat::BGRA;
}

AlphaPremultiplication ImageBufferHaikuSurfaceBackend::backendAlphaPremultiplication() const
{
return AlphaPremultiplication::Unpremultiplied;
}

GraphicsContext& ImageBufferHaikuSurfaceBackend::context() const
{
return *m_data.m_context;
Expand Down Expand Up @@ -179,208 +191,20 @@ void ImageBufferHaikuSurfaceBackend::drawPattern(GraphicsContext& destContext,
m_data.m_image->drawPattern(destContext, destRect, srcRect, patternTransform, phase, size, options.compositeOperator());
}

template <AlphaPremultiplication premultiplied>
RefPtr<ImageData> getData(const IntRect& rect, const IntRect& logicalRect, const ImageBufferData& data, const FloatSize& size, const FloatSize& logicalSize, float resolutionScale)
{
auto result = ImageData::create(rect.size());
auto* pixelArray = result ? result->data() : nullptr;
if (!result)
return nullptr;

// Can overflow, as we are adding 2 ints.
int endx = 0;
if (!WTF::safeAdd(rect.x(), rect.width(), endx))
return nullptr;

// Can overflow, as we are adding 2 ints.
int endy = 0;
if (!WTF::safeAdd(rect.y(), rect.height(), endy))
return nullptr;

if (rect.x() < 0 || rect.y() < 0 || endx > size.width() || endy > size.height())
pixelArray->zeroFill();

int originx = rect.x();
int destx = 0;
if (originx < 0) {
destx = -originx;
originx = 0;
}

if (endx > size.width())
endx = size.width();
int numColumns = endx - originx;

int originy = rect.y();
int desty = 0;
if (originy < 0) {
desty = -originy;
originy = 0;
}

if (endy > size.height())
endy = size.height();
int numRows = endy - originy;

// Nothing will be copied, so just return the result.
if (numColumns <= 0 || numRows <= 0)
return result;

// The size of the derived surface is in BackingStoreCoordinateSystem.
// We need to set the device scale for the derived surface from this ImageBuffer.
IntRect imageRect(originx, originy, numColumns, numRows);
NativeImagePtr imageSurface = data.m_bitmap;
//cairoSurfaceSetDeviceScale(imageSurface.get(), resolutionScale, resolutionScale);
originx = imageRect.x();
originy = imageRect.y();

unsigned char* dataSrc = (unsigned char*)imageSurface->Bits();
unsigned char* dataDst = pixelArray->data();
int stride = imageSurface->BytesPerRow();
unsigned destBytesPerRow = 4 * rect.width();

unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4;
for (int y = 0; y < numRows; ++y) {
unsigned* row = reinterpret_cast_ptr<unsigned*>(dataSrc + stride * (y + originy));
for (int x = 0; x < numColumns; x++) {
int basex = x * 4;
unsigned* pixel = row + x + originx;

// Avoid calling Color::colorFromPremultipliedARGB() because one
// function call per pixel is too expensive.
unsigned alpha = (*pixel & 0xFF000000) >> 24;
unsigned red = (*pixel & 0x00FF0000) >> 16;
unsigned green = (*pixel & 0x0000FF00) >> 8;
unsigned blue = (*pixel & 0x000000FF);

if (premultiplied == AlphaPremultiplication::Unpremultiplied) {
if (alpha && alpha != 255) {
red = red * 255 / alpha;
green = green * 255 / alpha;
blue = blue * 255 / alpha;
}
}

destRows[basex] = red;
destRows[basex + 1] = green;
destRows[basex + 2] = blue;
destRows[basex + 3] = alpha;
}
destRows += destBytesPerRow;
}

return result;
}

Vector<uint8_t> ImageBufferHaikuSurfaceBackend::toBGRAData() const
{
uint8_t* bits = (uint8_t*)m_data.m_bitmap->Bits();
Vector<uint8_t> v;
v.tryAppend(bits, m_data.m_bitmap->BitsLength());
return v;
return ImageBufferBackend::toBGRAData(m_data.m_bitmap->Bits());
}


RefPtr<ImageData> ImageBufferHaikuSurfaceBackend::getImageData(AlphaPremultiplication outputFormat, const IntRect& srcRect) const
{
IntRect logicalRect = srcRect;
IntRect backingStoreRect = srcRect;
backingStoreRect.scale(m_resolutionScale);

if (outputFormat == AlphaPremultiplication::Unpremultiplied)
return getData<AlphaPremultiplication::Unpremultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale);
return getData<AlphaPremultiplication::Premultiplied>(backingStoreRect, logicalRect, m_data, m_size, m_logicalSize, m_resolutionScale);
return ImageBufferBackend::getImageData(outputFormat, srcRect, m_data.m_bitmap->Bits());
}


void ImageBufferHaikuSurfaceBackend::putImageData(AlphaPremultiplication sourceFormat,
const ImageData& imageData, const IntRect& sourceRect, const IntPoint& destPoint,
AlphaPremultiplication)
void ImageBufferHaikuSurfaceBackend::putImageData(AlphaPremultiplication sourceFormat, const ImageData& imageData, const IntRect& sourceRect, const IntPoint& destPoint, AlphaPremultiplication)
{
IntRect logicalSourceRect = sourceRect;
IntPoint logicalDestPoint = destPoint;

IntRect scaledSourceRect = sourceRect;
IntSize scaledSourceSize = imageData.size();
IntPoint scaledDestPoint = destPoint;

scaledSourceRect.scale(m_resolutionScale);
scaledDestPoint.scale(m_resolutionScale);

ASSERT(scaledSourceRect.width() > 0);
ASSERT(scaledSourceRect.height() > 0);

int originx = scaledSourceRect.x();
int destx = scaledDestPoint.x() + scaledSourceRect.x();
int logicalDestx = logicalDestPoint.x() + logicalSourceRect.x();
ASSERT(destx >= 0);
ASSERT(destx < m_size.width());
ASSERT(originx >= 0);
ASSERT(originx <= scaledSourceRect.maxX());

int endx = scaledDestPoint.x() + scaledSourceRect.maxX();
int logicalEndx = logicalDestPoint.x() + logicalSourceRect.maxX();
ASSERT(endx <= m_size.width());

int numColumns = endx - destx;
int logicalNumColumns = logicalEndx - logicalDestx;

int originy = scaledSourceRect.y();
int desty = scaledDestPoint.y() + scaledSourceRect.y();
int logicalDesty = logicalDestPoint.y() + logicalSourceRect.y();
ASSERT(desty >= 0);
ASSERT(desty < m_size.height());
ASSERT(originy >= 0);
ASSERT(originy <= scaledSourceRect.maxY());

int endy = scaledDestPoint.y() + scaledSourceRect.maxY();
int logicalEndy = logicalDestPoint.y() + logicalSourceRect.maxY();
ASSERT(endy <= m_size.height());
int numRows = endy - desty;
int logicalNumRows = logicalEndy - logicalDesty;

// The size of the derived surface is in BackingStoreCoordinateSystem.
// We need to set the device scale for the derived surface from this ImageBuffer.
IntRect imageRect(destx, desty, numColumns, numRows);
NativeImagePtr imageSurface = m_data.m_bitmap;
//cairoSurfaceSetDeviceScale(imageSurface.get(), m_resolutionScale, m_resolutionScale);
destx = imageRect.x();
desty = imageRect.y();

uint8_t* pixelData = (uint8_t*)imageSurface->Bits();

unsigned srcBytesPerRow = 4 * scaledSourceSize.width();
int stride = imageSurface->BytesPerRow();

const uint8_t* srcRows = imageData.data()->data() + originy * srcBytesPerRow + originx * 4;
for (int y = 0; y < numRows; ++y) {
unsigned* row = reinterpret_cast_ptr<unsigned*>(pixelData + stride * (y + desty));
for (int x = 0; x < numColumns; x++) {
int basex = x * 4;
unsigned* pixel = row + x + destx;

// Avoid calling Color::premultipliedARGBFromColor() because one
// function call per pixel is too expensive.
unsigned red = srcRows[basex];
unsigned green = srcRows[basex + 1];
unsigned blue = srcRows[basex + 2];
unsigned alpha = srcRows[basex + 3];

if (sourceFormat == AlphaPremultiplication::Unpremultiplied) {
if (alpha != 255) {
red = (red * alpha + 254) / 255;
green = (green * alpha + 254) / 255;
blue = (blue * alpha + 254) / 255;
}
}

*pixel = (alpha << 24) | red << 16 | green << 8 | blue;
}
srcRows += srcBytesPerRow;
}

// This cairo surface operation is done in LogicalCoordinateSystem.
//cairo_surface_mark_dirty_rectangle(imageSurface.get(), logicalDestx, logicalDesty, logicalNumColumns, logicalNumRows);
ImageBufferBackend::putImageData(sourceFormat, imageData, sourceRect, destPoint, backendAlphaPremultiplication(), m_data.m_bitmap->Bits());
}

// TODO: PreserveResolution
Expand All @@ -397,7 +221,7 @@ String ImageBufferHaikuSurfaceBackend::toDataURL(const String& mimeType, Optiona
Vector<char> encodedBuffer;
base64Encode(binaryBuffer, encodedBuffer);

return "data:" + mimeType + ";base64;" + encodedBuffer;
return "data:" + mimeType + ";base64;" + encodedBuffer;
}


Expand Down Expand Up @@ -443,7 +267,7 @@ Vector<uint8_t> ImageBufferHaikuSurfaceBackend::toData(const String& mimeType, O
BMallocIO translatedStream;
// BBitmapStream doesn't take "const Bitmap*"...
BBitmapStream bitmapStream(m_data.m_bitmap.get());
BBitmap* tmp = NULL;
BBitmap* tmp = NULL;
if (roster->Translate(&bitmapStream, 0, 0, &translatedStream, translatorType,
B_TRANSLATOR_BITMAP, mimeType.utf8().data()) != B_OK) {
bitmapStream.DetachBitmap(&tmp);
Expand Down
Loading

0 comments on commit 4c12618

Please sign in to comment.