From b0822a83a3719ed32f6bf7f78c2ff1ba46837355 Mon Sep 17 00:00:00 2001 From: Tero Tiittanen Date: Thu, 17 Feb 2011 14:56:13 +0100 Subject: RGBA32 -> RGB565 dithering in Meego Graphicssystem, MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dithering in QMeeGoPixmapData::imageToEGLSharedImage is now done using Ordered Dithering, not anymore Floyd-Steinberg dithering. This provides better overall results. Fixes: NB#208927 - X dithering looks better than meego graphicssystem one Merge-request: 1079 Reviewed-by: Samuel Rødal --- src/plugins/graphicssystems/meego/dithering.cpp | 147 +++++++++--------------- 1 file changed, 52 insertions(+), 95 deletions(-) diff --git a/src/plugins/graphicssystems/meego/dithering.cpp b/src/plugins/graphicssystems/meego/dithering.cpp index 91e3337..2561c22 100644 --- a/src/plugins/graphicssystems/meego/dithering.cpp +++ b/src/plugins/graphicssystems/meego/dithering.cpp @@ -39,15 +39,22 @@ ** ****************************************************************************/ -// This is an implementation of the 32bit => 16bit Floyd-Steinberg dithering. +// Implements two dithering methods: +// +// * convertRGBA32_to_RGB565 +// +// This is implemented using Ordered Bayer Dithering. The code has been adapted +// from QX11PixmapData::fromImage. This method was originally implemented using +// Floyd-Steinberg dithering but was later changed to Ordered Dithering because +// of the better quality of the results. +// +// * convertRGBA32_to_RGBA4444 +// +// This is implemented using Floyd-Steinberg dithering. +// // The alghorithm used here is not the fastest possible but it's prolly fast enough: // uses look-up tables, integer-only arthmetics and works in one pass on two lines // at a time. It's a high-quality dithering using 1/8 diffusion precission. -// Two functions here to look at: -// -// * convertRGBA32_to_RGB565 -// * convertRGBA32_to_RGBA4444 -// // Each channel (RGBA) is diffused independently and alpha is dithered too. #include @@ -76,113 +83,63 @@ // Converts incoming RGB32 (QImage::Format_RGB32) to RGB565. Returns the newly allocated data. unsigned short* convertRGB32_to_RGB565(const unsigned char *in, int width, int height, int stride) { + static bool thresholdMapInitialized = false; + static int thresholdMap[16][16]; + + if (!thresholdMapInitialized) { + int i; + int j; + int n; + + thresholdMap[0][0] = 0; + thresholdMap[1][0] = 2; + thresholdMap[0][1] = 3; + thresholdMap[1][1] = 1; + + for (n=2; n<16; n*=2) { + for (i=0; i 0) alignedWidth++; // Will store output - unsigned short *out = (unsigned short *) malloc(alignedWidth * height * 2); - - // Lookup tables for the 8bit => 6bit and 8bit => 5bit conversion - unsigned char lookup_8bit_to_5bit[256]; - short lookup_8bit_to_5bit_diff[256]; - unsigned char lookup_8bit_to_6bit[256]; - short lookup_8bit_to_6bit_diff[256]; - - // Macros for the conversion using the lookup table. - #define CONVERT_8BIT_TO_5BIT(v) (lookup_8bit_to_5bit[v]) - #define DIFF_8BIT_TO_5BIT(v) (lookup_8bit_to_5bit_diff[v]) - - #define CONVERT_8BIT_TO_6BIT(v) (lookup_8bit_to_6bit[v]) - #define DIFF_8BIT_TO_6BIT(v) (lookup_8bit_to_6bit_diff[v]) - - int i; - int x, y, c; // Pixel we're processing. c is component number (0, 1, 2 for r, b, b) - short component[3]; // Stores the new components (r, g, b) for pixel produced during conversion - short diff; // The difference between the converted value and the original one. To be accumulated. - QVarLengthArray accumulatorData(3 * width * 2); // Data for three acumulators for r, g, b. Each accumulator is two lines. - short *accumulator[3]; // Helper for accessing the accumulator on a per-channel basis more easily. - accumulator[0] = accumulatorData.data(); - accumulator[1] = accumulatorData.data() + width; - accumulator[2] = accumulatorData.data() + (width * 2); - - // Produce the conversion lookup tables. - for (i = 0; i < 256; i++) { - lookup_8bit_to_5bit[i] = round(i / 8.0); - - // Before bitshifts: (i * 8) - (... * 8 * 8) - lookup_8bit_to_5bit_diff[i] = (i << 3) - (lookup_8bit_to_5bit[i] << 6); - if (lookup_8bit_to_5bit[i] > 31) - lookup_8bit_to_5bit[i] -= 1; - - lookup_8bit_to_6bit[i] = round(i / 4.0); - - // Before bitshifts: (i * 8) - (... * 4 * 8) - lookup_8bit_to_6bit_diff[i] = (i << 3) - (lookup_8bit_to_6bit[i] << 5); - if (lookup_8bit_to_6bit[i] > 63) - lookup_8bit_to_6bit[i] -= 1; - } + unsigned short *out = (unsigned short *)malloc (alignedWidth * height * 2); - // Clear the accumulators - memset(accumulator[0], 0, width * 4); - memset(accumulator[1], 0, width * 4); - memset(accumulator[2], 0, width * 4); + int x; + int y; + int threshold; // For each line... for (y = 0; y < height; y++) { - // For each accumulator, move the second line (index 1) to replace the first line (index 0). - // Clear the second line (index 1) - memcpy(accumulator[0], accumulator[0] + width, width * 2); - memset(accumulator[0] + width, 0, width * 2); - - memcpy(accumulator[1], accumulator[1] + width, width * 2); - memset(accumulator[1] + width, 0, width * 2); - - memcpy(accumulator[2], accumulator[2] + width, width * 2); - memset(accumulator[2] + width, 0, width * 2); - // For each column.... for (x = 0; x < width; x++) { - // For each component (r, g, b)... - for (c = 0; c < 3; c++) { + int r = GET_RGBA_COMPONENT(in, x, y, stride, 0); + int g = GET_RGBA_COMPONENT(in, x, y, stride, 1); + int b = GET_RGBA_COMPONENT(in, x, y, stride, 2); - // Get the 8bit value from the original image - component[c] = GET_RGBA_COMPONENT(in, x, y, stride, c); - - // Add the diffusion for this pixel we stored in the accumulator. - // >> 7 because the values in accumulator are stored * 128 - if (x != 0 && x != (width - 1)) { - if (accumulator[c][x] >> 7 != 0) - component[c] += rand() % accumulator[c][x] >> 7; - } - - // Make sure we're not over the boundaries. - CLAMP_256(component[c]); - - // For green component we use 6 bits. Otherwise 5 bits. - // Store the difference from converting 8bit => 6 bit and the orig pixel. - // Convert 8bit => 6(5) bit. - if (c == 1) { - diff = DIFF_8BIT_TO_6BIT(component[c]); - component[c] = CONVERT_8BIT_TO_6BIT(component[c]); - } else { - diff = DIFF_8BIT_TO_5BIT(component[c]); - component[c] = CONVERT_8BIT_TO_5BIT(component[c]); - } + threshold = thresholdMap[x%16][y%16]; - // Distribute the difference according to the matrix in the - // accumulation bufffer. - ACCUMULATE(accumulator[c], x + 1, 0, width, diff * 3); - ACCUMULATE(accumulator[c], x - 1, 1, width, diff * 5); - ACCUMULATE(accumulator[c], x, 1, width, diff * 5); - ACCUMULATE(accumulator[c], x + 1, 1, width, diff * 3); - } + if (r <= (255-(1<<3)) && ((r<<5) & 255) > threshold) r += (1<<3); + if (g <= (255-(1<<2)) && ((g<<6) & 255) > threshold) g += (1<<2); + if (b <= (255-(1<<3)) && ((b<<5) & 255) > threshold) b += (1<<3); // Write the newly produced pixel - PUT_565(out, x, y, alignedWidth, component[2], component[1], component[0]); + PUT_565(out, x, y, alignedWidth, ((b >> 3) & 0x1f), ((g >> 2) & 0x3f), ((r >> 3) & 0x1f)); } } -- cgit v0.12 >145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example widgets/analogclock
    \title Analog Clock Example

    The Analog Clock example shows how to draw the contents of a custom
    widget.

    \image analogclock-example.png Screenshot of the Analog Clock example

    This example also demonstrates how the transformation and scaling
    features of QPainter can be used to make drawing custom widgets
    easier.

    \section1 AnalogClock Class Definition

    The \c AnalogClock class provides a clock widget with hour and minute
    hands that is automatically updated every few seconds.
    We subclass \l QWidget and reimplement the standard
    \l{QWidget::paintEvent()}{paintEvent()} function to draw the clock face:

    \snippet examples/widgets/analogclock/analogclock.h 0

    \section1 AnalogClock Class Implementation

    \snippet examples/widgets/analogclock/analogclock.cpp 1

    When the widget is constructed, we set up a one-second timer to
    keep track of the current time, and we connect it to the standard
    \l{QWidget::update()}{update()} slot so that the clock face is
    updated when the timer emits the \l{QTimer::timeout()}{timeout()}
    signal.

    Finally, we resize the widget so that it is displayed at a
    reasonable size.

    \snippet examples/widgets/analogclock/analogclock.cpp 8
    \snippet examples/widgets/analogclock/analogclock.cpp 10

    The \c paintEvent() function is called whenever the widget's
    contents need to be updated. This happens when the widget is
    first shown, and when it is covered then exposed, but it is also
    executed when the  widget's \l{QWidget::update()}{update()} slot
    is called. Since we connected the timer's
    \l{QTimer::timeout()}{timeout()} signal to this slot, it will be
    called at least once every five seconds.

    Before we set up the painter and draw the clock, we first define
    two lists of \l {QPoint}s and two \l{QColor}s that will be used
    for the hour and minute hands. The minute hand's color has an
    alpha component of 191, meaning that it's 75% opaque.

    We also determine the length of the widget's shortest side so that we
    can fit the clock face inside the widget. It is also useful to determine
    the current time before we start drawing.

    \snippet examples/widgets/analogclock/analogclock.cpp 11
    \snippet examples/widgets/analogclock/analogclock.cpp 12
    \snippet examples/widgets/analogclock/analogclock.cpp 13
    \snippet examples/widgets/analogclock/analogclock.cpp 14

    The contents of custom widgets are drawn with a QPainter.
    Painters can be used to draw on any QPaintDevice, but they are
    usually used with widgets, so we pass the widget instance to the
    painter's constructor.

    We call QPainter::setRenderHint() with QPainter::Antialiasing to
    turn on antialiasing. This makes drawing of diagonal lines much
    smoother.

    The translation moves the origin to the center of the widget, and
    the scale operation ensures that the following drawing operations
    are scaled to fit within the widget. We use a scale factor that
    let's us use x and y coordinates between -100 and 100, and that
    ensures that these lie within the length of the widget's shortest
    side.

    To make our code simpler, we will draw a fixed size clock face that will
    be positioned and scaled so that it lies in the center of the widget.

    The painter takes care of all the transformations made during the
    paint event, and ensures that everything is drawn correctly. Letting
    the painter handle transformations is often easier than performing
    manual calculations just to draw the contents of a custom widget.

    \img analogclock-viewport.png

    We draw the hour hand first, using a formula that rotates the coordinate
    system counterclockwise by a number of degrees determined by the current
    hour and minute. This means that the hand will be shown rotated clockwise
    by the required amount.

    \snippet examples/widgets/analogclock/analogclock.cpp 15
    \snippet examples/widgets/analogclock/analogclock.cpp 16

    We set the pen to be Qt::NoPen because we don't want any outline,
    and we use a solid brush with the color appropriate for
    displaying hours. Brushes are used when filling in polygons and
    other geometric shapes.

    \snippet examples/widgets/analogclock/analogclock.cpp 17
    \snippet examples/widgets/analogclock/analogclock.cpp 19

    We save and restore the transformation matrix before and after the
    rotation because we want to place the minute hand without having to
    take into account any previous rotations.

    \snippet examples/widgets/analogclock/analogclock.cpp 20
    \codeline
    \snippet examples/widgets/analogclock/analogclock.cpp 21

    We draw markers around the edge of the clock for each hour. We
    draw each marker then rotate the coordinate system so that the
    painter is ready for the next one.

    \snippet examples/widgets/analogclock/analogclock.cpp 22
    \snippet examples/widgets/analogclock/analogclock.cpp 23

    The minute hand is rotated in a similar way to the hour hand.

    \snippet examples/widgets/analogclock/analogclock.cpp 25
    \codeline
    \snippet examples/widgets/analogclock/analogclock.cpp 26

    Again, we draw markers around the edge of the clock, but this
    time to indicate minutes. We skip multiples of 5 to avoid drawing
    minute markers on top of hour markers.
*/