From db5c9a5f9028fd87a74e1750068511bd311435ae Mon Sep 17 00:00:00 2001 From: Andreas Sandberg Date: Sat, 23 May 2015 13:37:03 +0100 Subject: [PATCH] base: Redesign internal frame buffer handling Currently, frame buffer handling in gem5 is quite ad hoc. In practice, we pass around naked pointers to raw pixel data and expect consumers to convert frame buffers using the (broken) VideoConverter. This changeset completely redesigns the way we handle frame buffers internally. In summary, it fixes several color conversion bugs, adds support for more color formats (e.g., big endian), and makes the code base easier to follow. In the new world, gem5 always represents pixel data using the Pixel struct when pixels need to be passed between different classes (e.g., a display controller and the VNC server). Producers of entire frames (e.g., display controllers) should use the FrameBuffer class to represent a frame. Frame producers are expected to create one instance of the FrameBuffer class in their constructors and register it with its consumers once. Consumers are expected to check the dimensions of the frame buffer when they consume it. Conversion between the external representation and the internal representation is supported for all common "true color" RGB formats of up to 32-bit color depth. The external pixel representation is expected to be between 1 and 4 bytes in either big endian or little endian. Color channels are assumed to be contiguous ranges of bits within each pixel word. The external pixel value is scaled to an 8-bit internal representation using a floating multiplication to map it to the entire 8-bit range. --- src/base/SConscript | 1 + src/base/bitmap.cc | 38 ++-- src/base/bitmap.hh | 51 +++--- src/base/framebuffer.cc | 173 +++++++++++++++++++ src/base/framebuffer.hh | 355 ++++++++++++++++++++++++++++++++++++++ src/base/types.hh | 5 + src/base/vnc/SConscript | 2 - src/base/vnc/convert.cc | 183 -------------------- src/base/vnc/convert.hh | 158 ----------------- src/base/vnc/vncinput.cc | 62 ++++--- src/base/vnc/vncinput.hh | 33 +--- src/base/vnc/vncserver.cc | 84 +++++---- src/base/vnc/vncserver.hh | 24 +-- src/dev/arm/hdlcd.cc | 124 +++++-------- src/dev/arm/hdlcd.hh | 18 +- src/dev/arm/pl111.cc | 103 ++++++----- src/dev/arm/pl111.hh | 13 +- src/sim/byteswap.hh | 2 - src/unittest/SConscript | 1 + src/unittest/fbtest.cc | 126 ++++++++++++++ 20 files changed, 924 insertions(+), 632 deletions(-) create mode 100644 src/base/framebuffer.cc create mode 100644 src/base/framebuffer.hh delete mode 100644 src/base/vnc/convert.cc delete mode 100644 src/base/vnc/convert.hh create mode 100644 src/unittest/fbtest.cc diff --git a/src/base/SConscript b/src/base/SConscript index e7c420f78..a00f8ad66 100644 --- a/src/base/SConscript +++ b/src/base/SConscript @@ -42,6 +42,7 @@ Source('cprintf.cc') Source('debug.cc') if env['USE_FENV']: Source('fenv.c') +Source('framebuffer.cc') Source('hostinfo.cc') Source('inet.cc') Source('inifile.cc') diff --git a/src/base/bitmap.cc b/src/base/bitmap.cc index d83a30be3..0052503a4 100644 --- a/src/base/bitmap.cc +++ b/src/base/bitmap.cc @@ -47,11 +47,8 @@ #include "base/misc.hh" // bitmap class ctor -Bitmap::Bitmap(VideoConvert::Mode mode, uint16_t w, uint16_t h, uint8_t *d) - : height(h), width(w), - header(getCompleteHeader()), - data(d), - vc(mode, VideoConvert::rgb8888, width, height) +Bitmap::Bitmap(const FrameBuffer *_fb) + : fb(*_fb) { } @@ -62,7 +59,7 @@ Bitmap::~Bitmap() const Bitmap::CompleteV1Header Bitmap::getCompleteHeader() const { - const uint32_t pixel_array_size(sizeof(PixelType) * width * height); + const uint32_t pixel_array_size(sizeof(PixelType) * fb.area()); const uint32_t file_size(sizeof(CompleteV1Header) + pixel_array_size); const CompleteV1Header header = { @@ -76,8 +73,8 @@ Bitmap::getCompleteHeader() const // Info/DIB header { sizeof(InfoHeaderV1), - width, - height, + fb.width(), + fb.height(), 1, /* Color planes */ 32, /* Bits per pixel */ 0, /* No compression */ @@ -93,28 +90,25 @@ Bitmap::getCompleteHeader() const } void -Bitmap::write(std::ostream *bmp) const +Bitmap::write(std::ostream &bmp) const { - assert(data); + const CompleteV1Header header(getCompleteHeader()); // 1. write the header - bmp->write(reinterpret_cast(&header), sizeof(header)); + bmp.write(reinterpret_cast(&header), sizeof(header)); // 2. write the bitmap data - const uint8_t *pixels(vc.convert(data)); - // BMP start store data left to right starting with the bottom row // so we need to do some creative flipping - for (int y = height - 1; y >= 0; y--) { - for (int x = 0; x < width; x++) { - bmp->write( - (const char *)&pixels[sizeof(PixelType) * (y * width + x)], - sizeof(PixelType)); - } - } + std::vector line_buffer(fb.width()); + for (int y = 0; y < fb.height(); ++y) { + for (unsigned x = 0; x < fb.width(); ++x) + line_buffer[x] = fb.pixel(x, fb.height() - y - 1); - bmp->flush(); + bmp.write(reinterpret_cast(line_buffer.data()), + line_buffer.size() * sizeof(line_buffer[0])); + } - delete[] pixels; + bmp.flush(); } diff --git a/src/base/bitmap.hh b/src/base/bitmap.hh index b06aff10f..0797a26a7 100644 --- a/src/base/bitmap.hh +++ b/src/base/bitmap.hh @@ -45,7 +45,7 @@ #include #include "base/compiler.hh" -#include "base/vnc/convert.hh" +#include "base/framebuffer.hh" /** * @file Declaration of a class that writes a frame buffer to a bitmap @@ -59,38 +59,17 @@ class Bitmap /** * Create a bitmap that takes data in a given mode & size and * outputs to an ostream. - * - * @param mode the type of data that is being provided - * @param h the hight of the image - * @param w the width of the image - * @param d the data for the image in mode */ - Bitmap(VideoConvert::Mode mode, uint16_t w, uint16_t h, uint8_t *d); + Bitmap(const FrameBuffer *fb); ~Bitmap(); - /** - * Provide the converter with the data that should be output. It - * will be converted into rgb8888 and written when write() is - * called. - * - * @param d the data - */ - void rawData(uint8_t* d) { data = d; } - /** * Write the frame buffer data into the provided ostream * * @param bmp stream to write to */ - void write(std::ostream *bmp) const; - - /** - * Gets a hash over the bitmap for quick comparisons to other bitmaps. - * - * @return hash of the bitmap - */ - uint64_t getHash() const { return vc.getHash(data); } + void write(std::ostream &bmp) const; private: @@ -121,16 +100,26 @@ class Bitmap InfoHeaderV1 info; } M5_ATTR_PACKED; - typedef uint32_t PixelType; + struct BmpPixel32 { + BmpPixel32 &operator=(const Pixel &rhs) { + red = rhs.red; + green = rhs.green; + blue = rhs.blue; + padding = 0; + + return *this; + } + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t padding; + } M5_ATTR_PACKED; - const CompleteV1Header getCompleteHeader() const; + typedef BmpPixel32 PixelType; - const uint16_t height; - const uint16_t width; - const CompleteV1Header header; - uint8_t *data; + const CompleteV1Header getCompleteHeader() const; - VideoConvert vc; + const FrameBuffer &fb; }; #endif // __BASE_BITMAP_HH__ diff --git a/src/base/framebuffer.cc b/src/base/framebuffer.cc new file mode 100644 index 000000000..dc4c2e1c2 --- /dev/null +++ b/src/base/framebuffer.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR 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. + * + * Authors: Andreas Sandberg + */ + +#include "base/framebuffer.hh" + +#include + +#include + +#include "base/bitfield.hh" + +const PixelConverter PixelConverter::rgba8888_le(4, 0, 8, 16, 8, 8, 8); +const PixelConverter PixelConverter::rgba8888_be(4, 0, 8, 16, 8, 8, 8, + BigEndianByteOrder); +const PixelConverter PixelConverter::rgb565_le(2, 0, 5, 11, 5, 6, 5); +const PixelConverter PixelConverter::rgb565_be(2, 0, 5, 11, 5, 6, 5, + BigEndianByteOrder); + +const FrameBuffer FrameBuffer::dummy(320, 240); + +PixelConverter::PixelConverter(unsigned _length, + unsigned ro, unsigned go, unsigned bo, + unsigned rw, unsigned gw, unsigned bw, + ByteOrder _byte_order) + : length(_length), + depth(rw + gw + bw), + byte_order(_byte_order), + ch_r(ro, rw), + ch_g(go, gw), + ch_b(bo, bw) +{ + assert(length > 1); +} + +PixelConverter::Channel::Channel(unsigned _offset, unsigned width) + : offset(_offset), + mask(::mask(width)), + factor(255.0 / mask) +{ +} + +uint32_t +PixelConverter::readWord(const uint8_t *p) const +{ + uint32_t word(0); + + if (byte_order == LittleEndianByteOrder) { + for (int i = 0; i < length; ++i) + word |= p[i] << (8 * i); + } else { + for (int i = 0; i < length; ++i) + word |= p[i] << (8 * (length - i - 1)); + } + + return word; +} + +void +PixelConverter::writeWord(uint8_t *p, uint32_t word) const +{ + if (byte_order == LittleEndianByteOrder) { + for (int i = 0; i < length; ++i) + p[i] = (word >> (8 * i)) & 0xFF; + } else { + for (int i = 0; i < length; ++i) + p[i] = (word >> (8 * (length - i - 1))) & 0xFF; + } +} + + + + +FrameBuffer::FrameBuffer(unsigned width, unsigned height) + : pixels(width * height), + _width(width), _height(height) +{ + clear(); +} + +FrameBuffer::FrameBuffer() + : _width(0), _height(0) +{ +} + +FrameBuffer::~FrameBuffer() +{ +} + +void +FrameBuffer::resize(unsigned width, unsigned height) +{ + _width = width; + _height = height; + + pixels.resize(width * height); +} + +void +FrameBuffer::fill(const Pixel &pixel) +{ + for (auto &p : pixels) + p = pixel; +} + +void +FrameBuffer::clear() +{ + static const Pixel black(0, 0, 0); + + fill(black); +} + +void +FrameBuffer::copyIn(const uint8_t *fb, const PixelConverter &conv) +{ + for (auto &p : pixels) { + p = conv.toPixel(fb); + fb += conv.length; + } +} + +void +FrameBuffer::copyOut(uint8_t *fb, const PixelConverter &conv) const +{ + for (auto &p : pixels) { + conv.fromPixel(fb, p); + fb += conv.length; + } +} + +uint64_t +FrameBuffer::getHash() const +{ + return adler32(0UL, + reinterpret_cast(pixels.data()), + area() * sizeof(Pixel)); +} diff --git a/src/base/framebuffer.hh b/src/base/framebuffer.hh new file mode 100644 index 000000000..457c6d06d --- /dev/null +++ b/src/base/framebuffer.hh @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR 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. + * + * Authors: Andreas Sandberg + */ + +#ifndef __BASE_FRAMEBUFFER_HH__ +#define __BASE_FRAMEBUFFER_HH__ + +#include +#include + +#include + +#include "base/types.hh" + +/** + * Internal gem5 representation of a Pixel. + */ +struct Pixel +{ + Pixel() + : red(0), green(0), blue(0), padding(0) {} + + Pixel(uint8_t _red, uint8_t _green, uint8_t _blue) + : red(_red), green(_green), blue(_blue), padding(0) {} + + uint8_t red; + uint8_t green; + uint8_t blue; + uint8_t padding; +}; + +inline bool +operator==(const Pixel &lhs, const Pixel &rhs) +{ + return lhs.red == rhs.red && + lhs.green == rhs.green && + lhs.blue == rhs.blue && + lhs.padding == rhs.padding; +} + + +/** + * Configurable RGB pixel converter. + * + * This class converts between external RGB representations and gem5's + * internal Pixel representation. The class assumes that pixels are + * stored in a word of configurable length (up to 32 bits). Individual + * pixels are assumed to be represented by contiguous bit ranges in + * the word (i.e., it is possible to shift and mask out a contiguous + * bit range for each pixel). + */ +class PixelConverter +{ + public: + /** + * Color channel conversion and scaling helper class. + */ + struct Channel { + /** + * @param offset Offset in bits. + * @param width Width in bits. + */ + Channel(unsigned offset, unsigned width); + + /** + * Get the value of a single color channel represented as an + * 8-bit number. + */ + uint8_t toPixel(uint32_t word) const { + return round(((word >> offset) & mask) * factor); + } + + /** + * Convert an 8-bit representation of a color into an external + * format. + */ + uint32_t fromPixel(uint8_t ch) const { + return (static_cast(round(ch / factor)) & mask) << offset; + } + + /** Offset in bits */ + unsigned offset; + /** Bit mask (after shifting) */ + unsigned mask; + /** + * Scaling factor when converting to the full range of an + * 8-bit color channel + */ + float factor; + }; + + PixelConverter(unsigned length, + unsigned ro, unsigned go, unsigned bo, + unsigned rw, unsigned gw, unsigned bw, + ByteOrder byte_order = LittleEndianByteOrder); + + /** Get the Pixel representation of a color word. */ + Pixel toPixel(uint32_t word) const { + return Pixel(ch_r.toPixel(word), + ch_g.toPixel(word), + ch_b.toPixel(word)); + } + + /** Get a Pixel representation by reading a word from memory. */ + Pixel toPixel(const uint8_t *rfb) const { + return toPixel(readWord(rfb)); + } + + /** Convert a Pixel into a color word */ + uint32_t fromPixel(const Pixel &pixel) const { + return ch_r.fromPixel(pixel.red) | + ch_g.fromPixel(pixel.green) | + ch_b.fromPixel(pixel.blue); + } + + /** + * Convert a pixel into a color word and store the resulting word + * in memory. + */ + void fromPixel(uint8_t *rfb, const Pixel &pixel) const { + writeWord(rfb, fromPixel(pixel)); + } + + /** + * Read a word of a given length and endianness from memory. + * + * The number of bytes read from memory is determined by the + * length of a color word. Note that some of the bytes may be + * padding. + * + * @param p Pointer to the first byte in the word. + * @return Word in host endianness. + */ + uint32_t readWord(const uint8_t *p) const; + /** + * Write a word of a given length and endianness to memory. + * + * @param p Pointer to the first byte in memory. + * @param word Word to store (host endianness). + */ + void writeWord(uint8_t *p, uint32_t word) const; + + /** Bytes per pixel when stored in memory (including padding) */ + unsigned length; + /** + * Number of bits used to represent one pixel value (excluding + * padding). This could be less than length * 8 if the pixel value + * is padded. + */ + unsigned depth; + /** Byte order when stored to memory. */ + ByteOrder byte_order; + + /** Red channel conversion helper */ + Channel ch_r; + /** Green channel conversion helper */ + Channel ch_g; + /** Blue channel conversion helper */ + Channel ch_b; + + /** Predefined 32-bit RGB (red in least significant bits, 8 + * bits/channel, little endian) conversion helper */ + static const PixelConverter rgba8888_le; + /** Predefined 16-bit RGB565 (red in least significant bits, + * little endian) conversion helper */ + static const PixelConverter rgb565_le; + + /** Predefined 32-bit RGB (red in least significant bits, 8 + * bits/channel, big endian) conversion helper */ + static const PixelConverter rgba8888_be; + /** Predefined 16-bit RGB565 (red in least significant bits, + * big endian) conversion helper */ + static const PixelConverter rgb565_be; +}; + +/** + * Internal gem5 representation of a frame buffer + * + * Display controllers and other devices producing images are expected + * to use this class to represent the final image. + * + * Pixels are indexed relative to the upper left corner of the + * image. That is, the pixel at position (0, 0) is the upper left + * corner. The backing store is a linear vector of Pixels ordered left + * to right starting in the upper left corner. + */ +class FrameBuffer +{ + public: + /** + * Create a frame buffer of a given size. + * + * @param width Width in pixels + * @param height Height in pixels + */ + FrameBuffer(unsigned width, unsigned height); + /** Create an empty (0x0) frame buffer */ + FrameBuffer(); + + virtual ~FrameBuffer(); + + /** + * Resize the frame buffer. + * + * This method resizes frame buffer including the backing + * store. The contents of the backing store are undefined after + * this operation. + * + * @param with Width in pixels. + * @param height Height in pixels. + */ + void resize(unsigned width, unsigned height); + + /** Frame buffer width in pixels */ + unsigned width() const { return _width; } + /** Frame buffer height in pixels */ + unsigned height() const { return _height; } + /** Total number of pixels in frame buffer */ + unsigned area() const { return _width * _height; } + + /** + * Fill the frame buffer with a single pixel value + * + * @param pixel Pixel value to fill with. + */ + void fill(const Pixel &pixel); + /** + * Fill the frame buffer with black pixels + */ + void clear(); + + /** + * Fill the frame buffer with pixel data from an external buffer + * of the same width and height as this frame buffer. + * + * @param fb External frame buffer + * @param conv Pixel conversion helper + */ + void copyIn(const uint8_t *fb, const PixelConverter &conv); + /** + * Fill the frame buffer with pixel data from an external buffer + * of the same width and height as this frame buffer. + * + * @param fb External frame buffer + * @param conv Pixel conversion helper + */ + void copyIn(const std::vector &fb, const PixelConverter &conv) { + copyIn(fb.data(), conv); + } + + /** + * Store the contents of this frame buffer in an external buffer + * of the same width and height as this frame buffer. + * + * @param fb External frame buffer + * @param conv Pixel conversion helper + */ + void copyOut(uint8_t *fb, const PixelConverter &conv) const; + /** + * Store the contents of this frame buffer in an external buffer + * of the same width and height as this frame buffer. + * + * @param fb External frame buffer + * @param conv Pixel conversion helper + */ + void copyOut(std::vector &fb, const PixelConverter &conv) const { + copyOut(fb.data(), conv); + } + + /** + * Get a pixel from an (x, y) coordinate + * + * @param x Distance from the left margin. + * @param y Distance from the top of the frame. + */ + const Pixel &pixel(unsigned x, unsigned y) const { + assert(x < _width); + assert(y < _height); + + return pixels[y * _width + x]; + } + + /** + * Get a pixel from an (x, y) coordinate + * + * @param x Distance from the left margin. + * @param y Distance from the top of the frame. + */ + Pixel &pixel(unsigned x, unsigned y) { + assert(x < _width); + assert(y < _height); + + return pixels[y * _width + x]; + } + + /** + * Create a hash of the image that can be used for quick + * comparisons. + */ + uint64_t getHash() const; + + /** + * Static "dummy" frame buffer. + * + * This is a dummy frame buffer that can be used as a place holder + * for devices that always expect a frame buffer to be present. + */ + static const FrameBuffer dummy; + + /** Frame buffer backing store */ + std::vector pixels; + + protected: + /** Width in pixels */ + unsigned _width; + /** Height in pixels */ + unsigned _height; +}; + +#endif // __BASE_FRAMEBUFFER_HH__ diff --git a/src/base/types.hh b/src/base/types.hh index c4cb4d988..2b6e3f11b 100644 --- a/src/base/types.hh +++ b/src/base/types.hh @@ -188,4 +188,9 @@ typedef std::shared_ptr Fault; constexpr decltype(nullptr) NoFault = nullptr; #endif +enum ByteOrder { + BigEndianByteOrder, + LittleEndianByteOrder +}; + #endif // __BASE_TYPES_HH__ diff --git a/src/base/vnc/SConscript b/src/base/vnc/SConscript index 416743200..271f894ce 100644 --- a/src/base/vnc/SConscript +++ b/src/base/vnc/SConscript @@ -39,8 +39,6 @@ Import('*') -Source('convert.cc') - SimObject('Vnc.py') Source('vncinput.cc') Source('vncserver.cc') diff --git a/src/base/vnc/convert.cc b/src/base/vnc/convert.cc deleted file mode 100644 index 2a52ccaec..000000000 --- a/src/base/vnc/convert.cc +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2011 ARM Limited - * All rights reserved - * - * The license below extends only to copyright in the software and shall - * not be construed as granting a license to any other intellectual - * property including but not limited to intellectual property relating - * to a hardware implementation of the functionality of the software - * licensed hereunder. You may use the software subject to the license - * terms below provided that you ensure that this notice is replicated - * unmodified and in its entirety in all distributions of the software, - * modified or unmodified, in source code or in binary form. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer; - * redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution; - * neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - * - * Authors: Ali Saidi - * William Wang - */ - -#include - -#include "base/vnc/convert.hh" -#include "base/misc.hh" - -/** @file - * This file provides conversion functions for a variety of video modes - */ - -VideoConvert::VideoConvert(Mode input_mode, Mode output_mode, int _width, - int _height) - : inputMode(input_mode), outputMode(output_mode), width(_width), - height(_height) -{ - if (inputMode != bgr565 && inputMode != rgb565 && - inputMode != bgr8888 && inputMode != bgr888) - fatal("Only support converting from bgr565, rdb565, " - "bgr8888 and bgr888\n"); - - if (outputMode != rgb8888) - fatal("Only support converting to rgb8888\n"); - - assert(0 < height && height < 4000); - assert(0 < width && width < 4000); -} - -VideoConvert::~VideoConvert() -{ -} - -uint8_t* -VideoConvert::convert(const uint8_t *fb) const -{ - switch (inputMode) { - case bgr565: - return m565rgb8888(fb, true); - case rgb565: - return m565rgb8888(fb, false); - case bgr8888: - return bgr8888rgb8888(fb); - case bgr888: - return bgr888rgb8888(fb); - default: - panic("Unimplemented Mode\n"); - } -} - -uint8_t* -VideoConvert::m565rgb8888(const uint8_t *fb, bool bgr) const -{ - uint8_t *out = new uint8_t[area() * sizeof(uint32_t)]; - uint32_t *out32 = (uint32_t*)out; - - uint16_t *in16 = (uint16_t*)fb; - - for (int x = 0; x < area(); x++) { - Bgr565 inpx; - Rgb8888 outpx = 0; - - inpx = in16[x]; - - if (bgr) { - outpx.red = inpx.blue << 3; - outpx.green = inpx.green << 2; - outpx.blue = inpx.red << 3; - } else { - outpx.blue = inpx.blue << 3; - outpx.green = inpx.green << 2; - outpx.red = inpx.red << 3; - } - - out32[x] = outpx; - } - - return out; -} - - -uint8_t* -VideoConvert::bgr8888rgb8888(const uint8_t *fb) const -{ - uint8_t *out = new uint8_t[area() * sizeof(uint32_t)]; - uint32_t *out32 = (uint32_t*)out; - - uint32_t *in32 = (uint32_t*)fb; - - for (int x = 0; x < area(); x++) { - Rgb8888 outpx = 0; - Bgr8888 inpx; - - - inpx = in32[x]; - - outpx.red = inpx.blue; - outpx.green = inpx.green; - outpx.blue = inpx.red; - - out32[x] = outpx; - } - - return out; -} - -uint8_t* -VideoConvert::bgr888rgb8888(const uint8_t *fb) const -{ - uint8_t *out = new uint8_t[area() * sizeof(uint32_t)]; - uint32_t *out32 = (uint32_t*)out; - - typedef uint8_t In24[3]; - const In24 *in24 = (In24 *)fb; - for (int x = 0; x < area(); x++) { - Rgb8888 outpx = 0; - - outpx.blue = in24[x][0]; - outpx.green = in24[x][1]; - outpx.red = in24[x][2]; - outpx.alpha = 0xFF; - - out32[x] = outpx; - } - - return out; -} - -/* -uint64_t -VideoConvert::getHash(const uint8_t *fb) const -{ - const uint8_t *fb_e = fb + area(); - - uint64_t hash = 1; - while (fb < fb_e - 8) { - hash += *((const uint64_t*)fb); - fb += 8; - } - - while (fb < fb_e) { - hash += *(fb++); - } - - return hash; -}*/ diff --git a/src/base/vnc/convert.hh b/src/base/vnc/convert.hh deleted file mode 100644 index 592076cbf..000000000 --- a/src/base/vnc/convert.hh +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2011 ARM Limited - * All rights reserved - * - * The license below extends only to copyright in the software and shall - * not be construed as granting a license to any other intellectual - * property including but not limited to intellectual property relating - * to a hardware implementation of the functionality of the software - * licensed hereunder. You may use the software subject to the license - * terms below provided that you ensure that this notice is replicated - * unmodified and in its entirety in all distributions of the software, - * modified or unmodified, in source code or in binary form. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer; - * redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution; - * neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR 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. - * - * Authors: Ali Saidi - */ - -/** @file - * This file provides conversion functions for a variety of video modes - */ - -#ifndef __BASE_VNC_CONVERT_HH__ -#define __BASE_VNC_CONVERT_HH__ - -#include -#include "base/bitunion.hh" - -class VideoConvert -{ - public: - enum Mode { - UnknownMode, - bgr565, - rgb565, - bgr8888, - rgb8888, - rgb888, - bgr888, - bgr444, - bgr4444, - rgb444, - rgb4444 - }; - - // supports bpp32 RGB (bmp) and bpp16 5:6:5 mode BGR (linux) - BitUnion32(Rgb8888) - Bitfield<7,0> blue; - Bitfield<15,8> green; - Bitfield<23,16> red; - Bitfield<31,24> alpha; - EndBitUnion(Rgb8888) - - BitUnion32(Bgr8888) - Bitfield<7,0> red; - Bitfield<15,8> green; - Bitfield<23,16> blue; - Bitfield<31,24> alpha; - EndBitUnion(Bgr8888) - - BitUnion16(Bgr565) - Bitfield<4,0> red; - Bitfield<10,5> green; - Bitfield<15,11> blue; - EndBitUnion(Bgr565) - - BitUnion16(Rgb565) - Bitfield<4,0> red; - Bitfield<10,5> green; - Bitfield<15,11> blue; - EndBitUnion(Rgb565) - - /** Setup the converter with the given parameters - * @param input_mode type of data that will be provided - * @param output_mode type of data that should be output - * @param _width width of the frame buffer - * @param _height height of the frame buffer - */ - VideoConvert(Mode input_mode, Mode output_mode, int _width, int _height); - - /** Destructor - */ - ~VideoConvert(); - - /** Convert the provided frame buffer data into the format specified in the - * constructor. - * @param fb the frame buffer to convert - * @return the converted data (user must free) - */ - uint8_t* convert(const uint8_t *fb) const; - - /** Return the number of pixels that this buffer specifies - * @return number of pixels - */ - int area() const { return width * height; } - - /** - * Returns a hash on the raw data. - * - * @return hash of the buffer - */ - inline uint64_t getHash(const uint8_t *fb) const { - return adler32(0UL, fb, width * height); - } - - private: - - /** - * Convert a bgr8888 input to rgb8888. - * @param fb the data to convert - * @return converted data - */ - uint8_t* bgr8888rgb8888(const uint8_t *fb) const; - - /** - * Convert a bgr888 input to rgb8888. - * @param fb the data to convert - * @return converted data - */ - uint8_t* bgr888rgb8888(const uint8_t *fb) const; - - /** - * Convert a bgr565 or rgb565 input to rgb8888. - * @param fb the data to convert - * @param bgr true if the input data is bgr565 - * @return converted data - */ - uint8_t* m565rgb8888(const uint8_t *fb, bool bgr) const; - - Mode inputMode; - Mode outputMode; - int width; - int height; -}; - -#endif // __BASE_VNC_CONVERT_HH__ - diff --git a/src/base/vnc/vncinput.cc b/src/base/vnc/vncinput.cc index 071804583..017fc3876 100644 --- a/src/base/vnc/vncinput.cc +++ b/src/base/vnc/vncinput.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -53,9 +53,10 @@ using namespace std; VncInput::VncInput(const Params *p) : SimObject(p), keyboard(NULL), mouse(NULL), - vc(NULL), fbPtr(NULL), videoMode(VideoConvert::UnknownMode), - _videoWidth(1), _videoHeight(1), captureEnabled(p->frame_capture), - captureCurrentFrame(0), captureLastHash(0), captureBitmap(0) + fb(&FrameBuffer::dummy), + _videoWidth(fb->width()), _videoHeight(fb->height()), + captureEnabled(p->frame_capture), + captureCurrentFrame(0), captureLastHash(0) { if (captureEnabled) { // remove existing frame output directory if it exists, then create a @@ -68,33 +69,40 @@ VncInput::VncInput(const Params *p) } void -VncInput::setFrameBufferParams(VideoConvert::Mode mode, uint16_t width, - uint16_t height) +VncInput::setFrameBuffer(const FrameBuffer *rfb) { - DPRINTF(VNC, "Updating video params: mode: %d width: %d height: %d\n", mode, - width, height); + if (!rfb) + panic("Trying to VNC frame buffer to NULL!"); - if (mode != videoMode || width != videoWidth() || height != videoHeight()) { - videoMode = mode; - _videoWidth = width; - _videoHeight = height; + fb = rfb; + + // create bitmap of the frame with new attributes + if (captureEnabled) + captureBitmap.reset(new Bitmap(rfb)); + + // Setting a new frame buffer means that we need to send an update + // to the client. Mark the internal buffers as dirty to do so. + setDirty(); +} - if (vc) - delete vc; +void +VncInput::setDirty() +{ + const unsigned width(fb->width()); + const unsigned height(fb->height()); - vc = new VideoConvert(mode, VideoConvert::rgb8888, videoWidth(), - videoHeight()); + if (_videoWidth != width || _videoHeight != height) { + DPRINTF(VNC, "Updating video params: width: %d height: %d\n", + width, height); - if (captureEnabled) { - // create bitmap of the frame with new attributes - if (captureBitmap) - delete captureBitmap; + _videoWidth = width; + _videoHeight = height; - assert(fbPtr); - captureBitmap = new Bitmap(videoMode, width, height, fbPtr); - assert(captureBitmap); - } + frameBufferResized(); } + + if (captureEnabled) + captureFrameBuffer(); } void @@ -103,7 +111,7 @@ VncInput::captureFrameBuffer() assert(captureBitmap); // skip identical frames - uint64_t new_hash = captureBitmap->getHash(); + uint64_t new_hash = fb->getHash(); if (captureLastHash == new_hash) return; captureLastHash = new_hash; @@ -116,8 +124,8 @@ VncInput::captureFrameBuffer() // create the compressed framebuffer file ostream *fb_out = simout.create(captureOutputDirectory + frameFilename, - true); - captureBitmap->write(fb_out); + true); + captureBitmap->write(*fb_out); simout.close(fb_out); ++captureCurrentFrame; diff --git a/src/base/vnc/vncinput.hh b/src/base/vnc/vncinput.hh index 1686e3f25..96235fec7 100644 --- a/src/base/vnc/vncinput.hh +++ b/src/base/vnc/vncinput.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -46,8 +46,8 @@ #define __BASE_VNC_VNC_INPUT_HH__ #include +#include -#include "base/vnc/convert.hh" #include "base/bitmap.hh" #include "params/VncInput.hh" #include "sim/sim_object.hh" @@ -160,11 +160,7 @@ class VncInput : public SimObject * tell us where the data is instead of constanly copying it around * @param rfb frame buffer that we're going to use */ - void - setFramebufferAddr(uint8_t* rfb) - { - fbPtr = rfb; - } + virtual void setFrameBuffer(const FrameBuffer *rfb); /** Set up the device that would like to receive notifications when keys are * pressed in the vnc client keyboard @@ -196,32 +192,19 @@ class VncInput : public SimObject * the frame buffer has been updated and a new image needs to be sent to the * client */ - virtual void setDirty() - { - if (captureEnabled) - captureFrameBuffer(); - } - - /** Set the mode of the data the frame buffer will be sending us - * @param mode the mode - */ - virtual void setFrameBufferParams(VideoConvert::Mode mode, uint16_t width, uint16_t height); + virtual void setDirty(); protected: + virtual void frameBufferResized() {}; + /** The device to notify when we get key events */ VncKeyboard *keyboard; /** The device to notify when we get mouse events */ VncMouse *mouse; - /** The video converter that transforms data for us */ - VideoConvert *vc; - /** pointer to the actual data that is stored in the frame buffer device */ - uint8_t* fbPtr; - - /** The mode of data we're getting frame buffer in */ - VideoConvert::Mode videoMode; + const FrameBuffer *fb; /** the width of the frame buffer we are sending to the client */ uint16_t _videoWidth; @@ -242,7 +225,7 @@ class VncInput : public SimObject uint64_t captureLastHash; /** Cached bitmap object for writing out frame buffers to file */ - Bitmap *captureBitmap; + std::unique_ptr captureBitmap; /** Captures the current frame buffer to a file */ void captureFrameBuffer(); diff --git a/src/base/vnc/vncserver.cc b/src/base/vnc/vncserver.cc index 6dc2f2f10..20f3bc8fe 100644 --- a/src/base/vnc/vncserver.cc +++ b/src/base/vnc/vncserver.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -61,6 +61,7 @@ #include #include +#include #include "base/atomicio.hh" #include "base/bitmap.hh" @@ -74,6 +75,12 @@ using namespace std; +const PixelConverter VncServer::pixelConverter( + 4, // 4 bytes / pixel + 16, 8, 0, // R in [23, 16], G in [15, 8], B in [7, 0] + 8, 8, 8, // 8 bits / channel + LittleEndianByteOrder); + /** @file * Implementiation of a VNC server */ @@ -122,20 +129,19 @@ VncServer::VncServer(const Params *p) curState = WaitForProtocolVersion; - // currently we only support this one pixel format - // unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha) - // keep it around for telling the client and making - // sure the client cooperates - pixelFormat.bpp = 32; - pixelFormat.depth = 24; - pixelFormat.bigendian = 0; + // We currently only support one pixel format. Extract the pixel + // representation from our PixelConverter instance and keep it + // around for telling the client and making sure it cooperates + pixelFormat.bpp = 8 * pixelConverter.length; + pixelFormat.depth = pixelConverter.depth; + pixelFormat.bigendian = pixelConverter.byte_order == BigEndianByteOrder; pixelFormat.truecolor = 1; - pixelFormat.redmax = 0xff; - pixelFormat.greenmax = 0xff; - pixelFormat.bluemax = 0xff; - pixelFormat.redshift = 16; - pixelFormat.greenshift = 8; - pixelFormat.blueshift = 0; + pixelFormat.redmax = pixelConverter.ch_r.mask; + pixelFormat.greenmax = pixelConverter.ch_g.mask; + pixelFormat.bluemax = pixelConverter.ch_b.mask; + pixelFormat.redshift = pixelConverter.ch_r.offset; + pixelFormat.greenshift = pixelConverter.ch_g.offset; + pixelFormat.blueshift = pixelConverter.ch_b.offset; DPRINTF(VNC, "Vnc server created at port %d\n", p->port); } @@ -615,13 +621,11 @@ void VncServer::sendFrameBufferUpdate() { - if (!fbPtr || dataFd <= 0 || curState != NormalPhase || !sendUpdate) { + if (dataFd <= 0 || curState != NormalPhase || !sendUpdate) { DPRINTF(VNC, "NOT sending framebuffer update\n"); return; } - assert(vc); - // The client will request data constantly, unless we throttle it sendUpdate = false; @@ -650,19 +654,25 @@ VncServer::sendFrameBufferUpdate() write(&fbu); write(&fbr); - assert(fbPtr); + assert(fb); - uint8_t *tmp = vc->convert(fbPtr); - uint64_t num_pixels = videoWidth() * videoHeight(); - write(tmp, num_pixels * sizeof(uint32_t)); - delete [] tmp; + std::vector line_buffer(pixelConverter.length * fb->width()); + for (int y = 0; y < fb->height(); ++y) { + // Convert and send a line at a time + uint8_t *raw_pixel(line_buffer.data()); + for (unsigned x = 0; x < fb->width(); ++x) { + pixelConverter.fromPixel(raw_pixel, fb->pixel(x, y)); + raw_pixel += pixelConverter.length; + } + write(line_buffer.data(), line_buffer.size()); + } } void VncServer::sendFrameBufferResized() { - assert(fbPtr && dataFd > 0 && curState == NormalPhase); + assert(fb && dataFd > 0 && curState == NormalPhase); DPRINTF(VNC, "Sending framebuffer resize\n"); FrameBufferUpdate fbu; @@ -692,19 +702,23 @@ VncServer::sendFrameBufferResized() } void -VncServer::setFrameBufferParams(VideoConvert::Mode mode, uint16_t width, - uint16_t height) +VncServer::setDirty() { - VncInput::setFrameBufferParams(mode, width, height); - - if (mode != videoMode || width != videoWidth() || height != videoHeight()) { - if (dataFd > 0 && fbPtr && curState == NormalPhase) { - if (supportsResizeEnc) - sendFrameBufferResized(); - else - // The frame buffer changed size and we can't update the client - detach(); - } + VncInput::setDirty(); + + sendUpdate = true; + sendFrameBufferUpdate(); +} + +void +VncServer::frameBufferResized() +{ + if (dataFd > 0 && curState == NormalPhase) { + if (supportsResizeEnc) + sendFrameBufferResized(); + else + // The frame buffer changed size and we can't update the client + detach(); } } diff --git a/src/base/vnc/vncserver.hh b/src/base/vnc/vncserver.hh index cd1f186f5..0222a7726 100644 --- a/src/base/vnc/vncserver.hh +++ b/src/base/vnc/vncserver.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -47,7 +47,6 @@ #include -#include "base/vnc/convert.hh" #include "base/vnc/vncinput.hh" #include "base/bitmap.hh" #include "base/circlebuf.hh" @@ -305,24 +304,11 @@ class VncServer : public VncInput */ void sendFrameBufferResized(); - public: - /** The frame buffer uses this call to notify the vnc server that - * the frame buffer has been updated and a new image needs to be sent to the - * client - */ - void - setDirty() - { - VncInput::setDirty(); - sendUpdate = true; - sendFrameBufferUpdate(); - } + static const PixelConverter pixelConverter; - /** Set the mode of the data the frame buffer will be sending us - * @param mode the mode - */ - void setFrameBufferParams(VideoConvert::Mode mode, uint16_t width, - uint16_t height); + public: + void setDirty() M5_ATTR_OVERRIDE; + void frameBufferResized() M5_ATTR_OVERRIDE; }; #endif diff --git a/src/dev/arm/hdlcd.cc b/src/dev/arm/hdlcd.cc index 37569b22b..b1c1c450b 100644 --- a/src/dev/arm/hdlcd.cc +++ b/src/dev/arm/hdlcd.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 ARM Limited + * Copyright (c) 2010-2013, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -37,15 +37,15 @@ * Authors: Chris Emmons */ +#include "dev/arm/hdlcd.hh" + #include "base/vnc/vncinput.hh" -#include "base/bitmap.hh" #include "base/output.hh" #include "base/trace.hh" #include "debug/HDLcd.hh" #include "debug/Uart.hh" #include "dev/arm/amba_device.hh" #include "dev/arm/base_gic.hh" -#include "dev/arm/hdlcd.hh" #include "mem/packet.hh" #include "mem/packet_access.hh" #include "sim/system.hh" @@ -63,10 +63,11 @@ HDLcd::HDLcd(const Params *p) h_sync(0), h_back_porch(0), h_data(0), h_front_porch(0), polarities(0), command(0), pixel_format(0), red_select(0), green_select(0), blue_select(0), - pixelClock(p->pixel_clock), vnc(p->vnc), bmp(NULL), pic(NULL), + pixelClock(p->pixel_clock), + fb(0, 0), vnc(p->vnc), bmp(&fb), pic(NULL), frameReadStartTime(0), dmaStartAddr(0), dmaCurAddr(0), dmaMaxAddr(0), dmaPendingNum(0), - frameUnderrun(false), virtualDisplayBuffer(NULL), pixelBufferSize(0), + frameUnderrun(false), pixelBufferSize(0), pixelIndex(0), doUpdateParams(false), frameUnderway(false), dmaBytesInFlight(0), startFrameEvent(this), endFrameEvent(this), renderPixelEvent(this), @@ -81,13 +82,11 @@ HDLcd::HDLcd(const Params *p) dmaDoneEventFree[i] = &dmaDoneEventAll[i]; if (vnc) - vnc->setFramebufferAddr(NULL); + vnc->setFrameBuffer(&fb); } HDLcd::~HDLcd() { - if (virtualDisplayBuffer) - delete [] virtualDisplayBuffer; } // read registers and frame buffer @@ -315,8 +314,14 @@ HDLcd::write(PacketPtr pkt) void HDLcd::updateVideoParams(bool unserializing = false) { - const uint16_t bpp = bytesPerPixel() << 3; - const size_t buffer_size = bytesPerPixel() * width() * height(); + const uint16_t bpp M5_VAR_USED = bytesPerPixel() << 3; + + // Workaround configuration bugs where multiple display + // controllers are attached to the same VNC server by reattaching + // enabled devices. This isn't ideal, but works as long as only + // one display controller is active at a time. + if (command.enable && vnc) + vnc->setFrameBuffer(&fb); // updating these parameters while LCD is enabled is not supported if (frameUnderway && !unserializing) @@ -328,18 +333,14 @@ HDLcd::updateVideoParams(bool unserializing = false) // there must be no outstanding DMA transactions for this to work if (!unserializing) { assert(dmaPendingNum == 0); - if (virtualDisplayBuffer) - delete [] virtualDisplayBuffer; - virtualDisplayBuffer = new uint8_t[buffer_size]; - memset(virtualDisplayBuffer, 0, buffer_size); - } - assert(virtualDisplayBuffer); - if (vnc) - vnc->setFramebufferAddr(virtualDisplayBuffer); + virtualDisplayBuffer.resize(bytesPerPixel() * area()); + fb.resize(width(), height()); + fb.clear(); - if (bmp) - delete bmp; + std::fill(virtualDisplayBuffer.begin(), virtualDisplayBuffer.end(), + 0); + } DPRINTF(HDLcd, "bpp = %d\n", bpp); DPRINTF(HDLcd, "display size = %d x %d\n", width(), height()); @@ -354,61 +355,11 @@ HDLcd::updateVideoParams(bool unserializing = false) DPRINTF(HDLcd, "simulated refresh rate ~ %.1ffps generating ~ %.1fMB/s " "traffic ([%.1fMHz, T=%d sim clocks] pclk, %d bpp => %.1fMB/s peak requirement)\n", fps, - fps * buffer_size / 1024 / 1024, + fps * virtualDisplayBuffer.size() / 1024 / 1024, (double)SimClock::Frequency / pixelClock / 1000000.0, pixelClock, bpp, (double)(SimClock::Frequency / pixelClock * (bpp / 8)) / 1024 / 1024); - - if (pixel_format.big_endian) - panic("Big Endian pixel format not implemented by HDLcd controller"); - - if (vnc) { - if ((bpp == 24) && - (red_select.size == 8) && - (blue_select.size == 8) && - (green_select.size == 8) && - (green_select.offset == 8)) { - if ((blue_select.offset == 0) && - (red_select.offset == 16)) { - vnc->setFrameBufferParams(VideoConvert::rgb8888, width(), - height()); - bmp = new Bitmap(VideoConvert::rgb8888, width(), height(), - virtualDisplayBuffer); - DPRINTF(HDLcd, "color mode: rgb888\n"); - } else if ((red_select.offset == 0) && - (blue_select.offset == 16)) { - vnc->setFrameBufferParams(VideoConvert::bgr8888, width(), - height()); - bmp = new Bitmap(VideoConvert::bgr8888, width(), height(), - virtualDisplayBuffer); - DPRINTF(HDLcd, "color mode: bgr888\n"); - } - } else if ((bpp == 16) && - (red_select.size == 5) && - (blue_select.size == 5) && - (green_select.size == 6) && - (green_select.offset == 5)) { - if ((blue_select.offset == 0) && - (red_select.offset == 11)) { - vnc->setFrameBufferParams(VideoConvert::rgb565, width(), - height()); - bmp = new Bitmap(VideoConvert::rgb565, width(), height(), - virtualDisplayBuffer); - DPRINTF(HDLcd, "color mode: rgb565\n"); - } else if ((red_select.offset == 0) && - (blue_select.offset == 11)) { - vnc->setFrameBufferParams(VideoConvert::bgr565, width(), - height()); - bmp = new Bitmap(VideoConvert::bgr565, width(), height(), - virtualDisplayBuffer); - DPRINTF(HDLcd, "color mode: bgr565\n"); - } - } else { - DPRINTF(HDLcd, "color mode: undefined\n"); - panic("Unimplemented video mode\n"); - } - } } void @@ -424,7 +375,7 @@ HDLcd::startFrame() doUpdateParams = false; } frameUnderway = true; - assert(virtualDisplayBuffer); + assert(!virtualDisplayBuffer.empty()); assert(pixelBufferSize == 0); assert(dmaBytesInFlight == 0); assert(dmaPendingNum == 0); @@ -484,10 +435,11 @@ HDLcd::fillPixelBuffer() // will be uncacheable as well. If we have uncacheable and cacheable // requests in the memory system for the same address it won't be // pleased + uint8_t *const dma_dst( + virtualDisplayBuffer.data() + dmaCurAddr - dmaStartAddr); event->setTransactionSize(transaction_size); dmaPort.dmaAction(MemCmd::ReadReq, dmaCurAddr, transaction_size, event, - virtualDisplayBuffer + dmaCurAddr - dmaStartAddr, - 0, Request::UNCACHEABLE); + dma_dst, 0, Request::UNCACHEABLE); dmaCurAddr += transaction_size; dmaBytesInFlight += transaction_size; } @@ -549,6 +501,16 @@ HDLcd::renderPixel() schedule(renderPixelEvent, nextEventTick); } +PixelConverter +HDLcd::pixelConverter() const +{ + return PixelConverter( + bytesPerPixel(), + red_select.offset, green_select.offset, blue_select.offset, + red_select.size, green_select.size, blue_select.size, + pixel_format.big_endian ? BigEndianByteOrder : LittleEndianByteOrder); +} + void HDLcd::endFrame() { assert(pixelBufferSize == 0); @@ -556,6 +518,8 @@ HDLcd::endFrame() { assert(dmaBytesInFlight == 0); assert(dmaDoneEventFree.size() == dmaDoneEventAll.size()); + fb.copyIn(virtualDisplayBuffer, pixelConverter()); + if (vnc) vnc->setDirty(); @@ -563,10 +527,9 @@ HDLcd::endFrame() { if (!pic) pic = simout.create(csprintf("%s.framebuffer.bmp", sys->name()), true); - assert(bmp); assert(pic); pic->seekp(0); - bmp->write(pic); + bmp.write(*pic); } // start the next frame @@ -664,8 +627,7 @@ HDLcd::serialize(std::ostream &os) SERIALIZE_SCALAR(dmaPendingNum); SERIALIZE_SCALAR(frameUnderrun); - const size_t buffer_size = bytesPerPixel() * width() * height(); - SERIALIZE_ARRAY(virtualDisplayBuffer, buffer_size); + arrayParamOut(os, "virtualDisplayBuffer", virtualDisplayBuffer); SERIALIZE_SCALAR(pixelBufferSize); SERIALIZE_SCALAR(pixelIndex); @@ -777,9 +739,7 @@ HDLcd::unserialize(Checkpoint *cp, const std::string §ion) UNSERIALIZE_SCALAR(frameUnderrun); UNSERIALIZE_SCALAR(dmaBytesInFlight); - const size_t buffer_size = bytesPerPixel() * width() * height(); - virtualDisplayBuffer = new uint8_t[buffer_size]; - UNSERIALIZE_ARRAY(virtualDisplayBuffer, buffer_size); + arrayParamIn(cp, section, "virtualDisplayBuffer", virtualDisplayBuffer); UNSERIALIZE_SCALAR(pixelBufferSize); UNSERIALIZE_SCALAR(pixelIndex); @@ -823,6 +783,8 @@ HDLcd::unserialize(Checkpoint *cp, const std::string §ion) if (frameUnderway) { updateVideoParams(true); + fb.resize(width(), height()); + fb.copyIn(virtualDisplayBuffer, pixelConverter()); if (vnc) vnc->setDirty(); } diff --git a/src/dev/arm/hdlcd.hh b/src/dev/arm/hdlcd.hh index ba22cc163..61d2dc5d7 100644 --- a/src/dev/arm/hdlcd.hh +++ b/src/dev/arm/hdlcd.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 ARM Limited + * Copyright (c) 2010-2013, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -83,13 +83,15 @@ #define __DEV_ARM_HDLCD_HH__ #include +#include +#include "base/bitmap.hh" +#include "base/framebuffer.hh" #include "dev/arm/amba_device.hh" #include "params/HDLcd.hh" #include "sim/serialize.hh" class VncInput; -class Bitmap; class HDLcd: public AmbaDmaDevice { @@ -142,6 +144,8 @@ class HDLcd: public AmbaDmaDevice /** AXI port width in bytes */ static const size_t AXI_PORT_WIDTH = 8; + static const size_t MAX_BURST_SIZE = MAX_BURST_LEN * AXI_PORT_WIDTH; + /** * @name RegisterFieldLayouts * Bit layout declarations for multi-field registers. @@ -242,11 +246,13 @@ class HDLcd: public AmbaDmaDevice /** Pixel clock period */ const Tick pixelClock; + FrameBuffer fb; + /** VNC server */ VncInput *vnc; /** Helper to write out bitmaps */ - Bitmap *bmp; + Bitmap bmp; /** Picture of what the current frame buffer looks like */ std::ostream *pic; @@ -325,7 +331,7 @@ class HDLcd: public AmbaDmaDevice bool frameUnderrun; /** HDLcd virtual display buffer */ - uint8_t *virtualDisplayBuffer; + std::vector virtualDisplayBuffer; /** Size of the pixel buffer */ size_t pixelBufferSize; @@ -402,6 +408,8 @@ class HDLcd: public AmbaDmaDevice return fb_line_count.fb_line_count; } + inline size_t area() const { return height() * width(); } + /** * Gets the total number of pixel clocks per display line. * @@ -436,6 +444,8 @@ class HDLcd: public AmbaDmaDevice /** Called when it is time to render a pixel */ void renderPixel(); + PixelConverter pixelConverter() const; + /** Start of frame event */ EventWrapper startFrameEvent; diff --git a/src/dev/arm/pl111.cc b/src/dev/arm/pl111.cc index a5b9b412c..fcce09f50 100644 --- a/src/dev/arm/pl111.cc +++ b/src/dev/arm/pl111.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012 ARM Limited + * Copyright (c) 2010-2012, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -39,7 +39,6 @@ */ #include "base/vnc/vncinput.hh" -#include "base/bitmap.hh" #include "base/output.hh" #include "base/trace.hh" #include "debug/PL111.hh" @@ -63,7 +62,9 @@ Pl111::Pl111(const Params *p) clcdCrsrCtrl(0), clcdCrsrConfig(0), clcdCrsrPalette0(0), clcdCrsrPalette1(0), clcdCrsrXY(0), clcdCrsrClip(0), clcdCrsrImsc(0), clcdCrsrIcr(0), clcdCrsrRis(0), clcdCrsrMis(0), - pixelClock(p->pixel_clock), vnc(p->vnc), bmp(NULL), pic(NULL), + pixelClock(p->pixel_clock), + converter(PixelConverter::rgba8888_le), fb(LcdMaxWidth, LcdMaxHeight), + vnc(p->vnc), bmp(&fb), pic(NULL), width(LcdMaxWidth), height(LcdMaxHeight), bytesPerPixel(4), startTime(0), startAddr(0), maxAddr(0), curAddr(0), waterMark(0), dmaPendingNum(0), readEvent(this), fillFifoEvent(this), @@ -83,7 +84,7 @@ Pl111::Pl111(const Params *p) dmaDoneEventFree[i] = &dmaDoneEventAll[i]; if (vnc) - vnc->setFramebufferAddr(dmaBuffer); + vnc->setFrameBuffer(&fb); } Pl111::~Pl111() @@ -378,45 +379,66 @@ Pl111::write(PacketPtr pkt) return pioDelay; } +PixelConverter +Pl111::pixelConverter() const +{ + unsigned rw, gw, bw; + unsigned offsets[3]; + + switch (lcdControl.lcdbpp) { + case bpp24: + rw = gw = bw = 8; + offsets[0] = 0; + offsets[1] = 8; + offsets[2] = 16; + break; + + case bpp16m565: + rw = 5; + gw = 6; + bw = 5; + offsets[0] = 0; + offsets[1] = 5; + offsets[2] = 11; + break; + + default: + panic("Unimplemented video mode\n"); + } + + if (lcdControl.bgr) { + return PixelConverter( + bytesPerPixel, + offsets[2], offsets[1], offsets[0], + rw, gw, bw, + LittleEndianByteOrder); + } else { + return PixelConverter( + bytesPerPixel, + offsets[0], offsets[1], offsets[2], + rw, gw, bw, + LittleEndianByteOrder); + } +} + void Pl111::updateVideoParams() { - if (lcdControl.lcdbpp == bpp24) { - bytesPerPixel = 4; - } else if (lcdControl.lcdbpp == bpp16m565) { - bytesPerPixel = 2; - } + if (lcdControl.lcdbpp == bpp24) { + bytesPerPixel = 4; + } else if (lcdControl.lcdbpp == bpp16m565) { + bytesPerPixel = 2; + } - if (vnc) { - if (lcdControl.lcdbpp == bpp24 && lcdControl.bgr) - vnc->setFrameBufferParams(VideoConvert::bgr8888, width, - height); - else if (lcdControl.lcdbpp == bpp24 && !lcdControl.bgr) - vnc->setFrameBufferParams(VideoConvert::rgb8888, width, - height); - else if (lcdControl.lcdbpp == bpp16m565 && lcdControl.bgr) - vnc->setFrameBufferParams(VideoConvert::bgr565, width, - height); - else if (lcdControl.lcdbpp == bpp16m565 && !lcdControl.bgr) - vnc->setFrameBufferParams(VideoConvert::rgb565, width, - height); - else - panic("Unimplemented video mode\n"); - } + fb.resize(width, height); + converter = pixelConverter(); - if (bmp) - delete bmp; - - if (lcdControl.lcdbpp == bpp24 && lcdControl.bgr) - bmp = new Bitmap(VideoConvert::bgr8888, width, height, dmaBuffer); - else if (lcdControl.lcdbpp == bpp24 && !lcdControl.bgr) - bmp = new Bitmap(VideoConvert::rgb8888, width, height, dmaBuffer); - else if (lcdControl.lcdbpp == bpp16m565 && lcdControl.bgr) - bmp = new Bitmap(VideoConvert::bgr565, width, height, dmaBuffer); - else if (lcdControl.lcdbpp == bpp16m565 && !lcdControl.bgr) - bmp = new Bitmap(VideoConvert::rgb565, width, height, dmaBuffer); - else - panic("Unimplemented video mode\n"); + // Workaround configuration bugs where multiple display + // controllers are attached to the same VNC server by reattaching + // enabled devices. This isn't ideal, but works as long as only + // one display controller is active at a time. + if (lcdControl.lcdpwr && vnc) + vnc->setFrameBuffer(&fb); } void @@ -493,6 +515,7 @@ Pl111::dmaDone() } assert(!readEvent.scheduled()); + fb.copyIn(dmaBuffer, converter); if (vnc) vnc->setDirty(); @@ -502,10 +525,9 @@ Pl111::dmaDone() if (!pic) pic = simout.create(csprintf("%s.framebuffer.bmp", sys->name()), true); - assert(bmp); assert(pic); pic->seekp(0); - bmp->write(pic); + bmp.write(*pic); } // schedule the next read based on when the last frame started @@ -721,6 +743,7 @@ Pl111::unserialize(Checkpoint *cp, const std::string §ion) if (lcdControl.lcdpwr) { updateVideoParams(); + fb.copyIn(dmaBuffer, converter); if (vnc) vnc->setDirty(); } diff --git a/src/dev/arm/pl111.hh b/src/dev/arm/pl111.hh index a7c158473..85973bbc7 100644 --- a/src/dev/arm/pl111.hh +++ b/src/dev/arm/pl111.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012 ARM Limited + * Copyright (c) 2010-2012, 2015 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -47,13 +47,15 @@ #define __DEV_ARM_PL111_HH__ #include +#include +#include "base/bitmap.hh" +#include "base/framebuffer.hh" #include "dev/arm/amba_device.hh" #include "params/Pl111.hh" #include "sim/serialize.hh" class VncInput; -class Bitmap; class Pl111: public AmbaDmaDevice { @@ -256,11 +258,14 @@ class Pl111: public AmbaDmaDevice /** Pixel clock */ Tick pixelClock; + PixelConverter converter; + FrameBuffer fb; + /** VNC server */ VncInput *vnc; /** Helper to write out bitmaps */ - Bitmap *bmp; + Bitmap bmp; /** Picture of what the current frame buffer looks like */ std::ostream *pic; @@ -295,6 +300,8 @@ class Pl111: public AmbaDmaDevice /** Number of pending dma reads */ uint32_t dmaPendingNum; + PixelConverter pixelConverter() const; + /** Send updated parameters to the vnc server */ void updateVideoParams(); diff --git a/src/sim/byteswap.hh b/src/sim/byteswap.hh index b46c1e552..7e5d6809e 100644 --- a/src/sim/byteswap.hh +++ b/src/sim/byteswap.hh @@ -58,8 +58,6 @@ #include #endif -enum ByteOrder {BigEndianByteOrder, LittleEndianByteOrder}; - //These functions actually perform the swapping for parameters //of various bit lengths inline uint64_t diff --git a/src/unittest/SConscript b/src/unittest/SConscript index 2926311a5..ae22225b3 100644 --- a/src/unittest/SConscript +++ b/src/unittest/SConscript @@ -37,6 +37,7 @@ UnitTest('bitvectest', 'bitvectest.cc') UnitTest('circletest', 'circletest.cc') UnitTest('cprintftest', 'cprintftest.cc') UnitTest('cprintftime', 'cprintftest.cc') +UnitTest('fbtest', 'fbtest.cc') UnitTest('initest', 'initest.cc') UnitTest('nmtest', 'nmtest.cc') UnitTest('rangemaptest', 'rangemaptest.cc') diff --git a/src/unittest/fbtest.cc b/src/unittest/fbtest.cc new file mode 100644 index 000000000..8a8f959ae --- /dev/null +++ b/src/unittest/fbtest.cc @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2015 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR 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. + * + * Authors: Andreas Sandberg + */ + +#include "base/framebuffer.hh" +#include "unittest/unittest.hh" + +static Pixel pixel_red(0xff, 0x00, 0x00); +static Pixel pixel_green(0x00, 0xff, 0x00); +static Pixel pixel_blue(0x00, 0x00, 0xff); + +int +main() +{ + UnitTest::setCase("Pixel conversion RGBA8888"); + { + EXPECT_EQ(PixelConverter::rgba8888_le.fromPixel(pixel_red), + 0x000000ffU); + EXPECT_EQ(PixelConverter::rgba8888_le.fromPixel(pixel_green), + 0x0000ff00U); + EXPECT_EQ(PixelConverter::rgba8888_le.fromPixel(pixel_blue), + 0x00ff0000U); + + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(0x000000ffU), + pixel_red); + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(0x0000ff00U), + pixel_green); + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(0x00ff0000U), + pixel_blue); + } + + UnitTest::setCase("Pixel conversion RGB565"); + { + EXPECT_EQ(PixelConverter::rgb565_le.fromPixel(pixel_red), 0x001fU); + EXPECT_EQ(PixelConverter::rgb565_le.fromPixel(pixel_green), 0x07e0U); + EXPECT_EQ(PixelConverter::rgb565_le.fromPixel(pixel_blue), 0xf800U); + + EXPECT_EQ(PixelConverter::rgb565_le.toPixel(0x001fU), pixel_red); + EXPECT_EQ(PixelConverter::rgb565_le.toPixel(0x07e0U), pixel_green); + EXPECT_EQ(PixelConverter::rgb565_le.toPixel(0xf800U), pixel_blue); + } + + UnitTest::setCase("Pixel->Mem RGBA8888 LE"); + { + uint8_t data[] = { 0xde, 0xad, 0xbe, 0xef }; + PixelConverter::rgba8888_le.fromPixel(data, pixel_red); + EXPECT_EQ(data[0], 0xff); + EXPECT_EQ(data[1], 0x00); + EXPECT_EQ(data[3], 0x00); + EXPECT_EQ(data[3], 0x00); + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(data), pixel_red); + + PixelConverter::rgba8888_le.fromPixel(data, pixel_green); + EXPECT_EQ(data[0], 0x00); + EXPECT_EQ(data[1], 0xff); + EXPECT_EQ(data[3], 0x00); + EXPECT_EQ(data[3], 0x00); + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(data), pixel_green); + + PixelConverter::rgba8888_le.fromPixel(data, pixel_blue); + EXPECT_EQ(data[0], 0x00); + EXPECT_EQ(data[1], 0x00); + EXPECT_EQ(data[2], 0xff); + EXPECT_EQ(data[3], 0x00); + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(data), pixel_blue); + } + + UnitTest::setCase("Mem->Pixel RGBA8888 LE"); + { + uint8_t red[] = { 0xff, 0x00, 0x00, 0x00 }; + uint8_t green[] = { 0x00, 0xff, 0x00, 0x00 }; + uint8_t blue[] = { 0x00, 0x00, 0xff, 0x00 }; + + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(red), pixel_red); + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(green), pixel_green); + EXPECT_EQ(PixelConverter::rgba8888_le.toPixel(blue), pixel_blue); + } + + UnitTest::setCase("Mem->Pixel RGBA8888 BE"); + { + uint8_t red[] = { 0x00, 0x00, 0x00, 0xff }; + uint8_t green[] = { 0x00, 0x00, 0xff, 0x00 }; + uint8_t blue[] = { 0x00, 0xff, 0x00, 0x00 }; + + EXPECT_EQ(PixelConverter::rgba8888_be.toPixel(red), pixel_red); + EXPECT_EQ(PixelConverter::rgba8888_be.toPixel(green), pixel_green); + EXPECT_EQ(PixelConverter::rgba8888_be.toPixel(blue), pixel_blue); + } + + return UnitTest::printResults(); +} -- 2.30.2