Skip to content

Commit

Permalink
Merge pull request #9 from stissing/master
Browse files Browse the repository at this point in the history
Added a lens effect
  • Loading branch information
zrrbite committed May 11, 2015
2 parents ec98bcc + 56a1908 commit 3be8493
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -36,7 +36,7 @@ The TouchGFX version stated is the version that the component has been tested wi
<tr>
<td>WheelSelector <br>TouchGFX 4.2<br> <a href="widgets/WheelSelector/"> <img src="widgets/WheelSelector/days_fade_in.png" height="100"></a></td>
<td>CircularProgress <br>TouchGFX 4.2<br> <a href="widgets/CircularProgress/"> <img src="widgets/CircularProgress/example.png" height="100"></a></td>
<td></td>
<td>Lens <br>TouchGFX 4.2 <br><a href="widgets/Lens/"><img src="widgets/Lens/screenshots/lens-effect.png" height="100"></a></td>
</tr>
</table>

Expand Down
105 changes: 105 additions & 0 deletions widgets/Lens/Lens.cpp
@@ -0,0 +1,105 @@
#include <gui/common/Lens.hpp>
#include <touchgfx/hal/HAL.hpp>
#include <math.h>
#include <BitmapDatabase.hpp>

Lens::Lens()
{
// calculate the distortion of the lens
// derivations of the formula can be found online, for instance in the demo effects collection at
// http://demo-effects.cvs.sourceforge.net/viewvc/demo-effects/demo-effects/LENS/lens.c?view=markup
for (int y = 0; y < lensRadius; y++) {
for (int x = 0; x < lensRadius; x++) {
int ix, iy, offset;
if ((x * x + y * y) < (lensRadius * lensRadius))
{
float shift = lensZoom/sqrt((float)lensZoom*lensZoom - (x*x + y*y - lensRadius*lensRadius));
ix = (int)(x * shift - x);
iy = (int)(y * shift - y);
}
else
{
ix = 0;
iy = 0;
}
offset = (iy * touchgfx::HAL::DISPLAY_WIDTH + ix);
lens[lensRadius - y][lensRadius - x] = -offset;
lens[lensRadius + y][lensRadius + x] = offset;
offset = (-iy * touchgfx::HAL::DISPLAY_WIDTH + ix);
lens[lensRadius + y][lensRadius - x] = -offset;
lens[lensRadius - y][lensRadius + x] = offset;
}
}
setWidth(lensWidth);
setHeight(lensWidth);

lensImage.setBitmap(touchgfx::Bitmap(BITMAP_LENS_IMAGE_ID));
}

touchgfx::Rect Lens::getSolidRect() const
{
return touchgfx::Rect();
}

void Lens::draw(const touchgfx::Rect& invalidatedArea) const
{
uint16_t *fb = touchgfx::HAL::getInstance()->lockFrameBuffer();

touchgfx::Rect abs(0,0,getWidth(),getHeight());
translateRectToAbsolute(abs);

// since we both read and write the frame buffer, we split the loops in to four sections/quadrants
// and run through them left to right or right to left, top to bottom or bottom to top,
int16_t middleX = getWidth()/2;
int16_t middleY = getHeight()/2;

for(int y = invalidatedArea.y; y < invalidatedArea.bottom() && y <= middleY; y++)
{
for(int x = invalidatedArea.x; x < invalidatedArea.right() && x <= middleX; x++)
{
apply(fb, abs.x, abs.y, x, y);
}

for(int x = invalidatedArea.right()-1; x > middleX && x >= invalidatedArea.x; x--)
{
apply(fb, abs.x, abs.y, x, y);
}
}

for(int y = invalidatedArea.bottom()-1; y > middleY && y >= invalidatedArea.y; y--)
{
for(int x = invalidatedArea.x; x < invalidatedArea.right() && x <= middleX; x++)
{
apply(fb, abs.x, abs.y, x, y);
}

for(int x = invalidatedArea.right()-1; x > middleX && x >= invalidatedArea.x; x--)
{
apply(fb, abs.x, abs.y, x, y);
}
}

touchgfx::HAL::getInstance()->unlockFrameBuffer();

touchgfx::Rect dirtyBitmapArea = touchgfx::Bitmap(lensImage.getBitmap()).getRect() & invalidatedArea;
touchgfx::HAL::lcd().drawPartialBitmap(touchgfx::Bitmap(lensImage.getBitmap()), abs.x, abs.y, dirtyBitmapArea, 255);
}

void Lens::apply(uint16_t* fb, int absx, int absy, int x, int y) const
{
int16_t lensOffset = lens[y][x];
uint32_t fbOffset = x+absx + (y+absy) * touchgfx::HAL::DISPLAY_WIDTH;
if((int32_t)fbOffset + lensOffset < 0)
{
fb[fbOffset] = fb[fbOffset + lensOffset + touchgfx::HAL::DISPLAY_HEIGHT*touchgfx::HAL::DISPLAY_WIDTH];
}
else if((int32_t)fbOffset + lensOffset > touchgfx::HAL::DISPLAY_HEIGHT*touchgfx::HAL::DISPLAY_WIDTH)
{
fb[fbOffset] = fb[fbOffset + lensOffset - touchgfx::HAL::DISPLAY_HEIGHT*touchgfx::HAL::DISPLAY_WIDTH];
}
else
{
fb[fbOffset] = fb[fbOffset + lensOffset];
}
}

62 changes: 62 additions & 0 deletions widgets/Lens/Lens.hpp
@@ -0,0 +1,62 @@
#ifndef LENS_HPP
#define LENS_HPP

#include <touchgfx/widgets/Widget.hpp>
#include <touchgfx/hal/Types.hpp>
#include <touchgfx/widgets/Image.hpp>

/**
* @class Lens
*
* Lens shows a lens like effect on top of the frame buffer
*
* This widget is part of the TouchGFX Open Widget Repository.
* https://github.com/draupnergraphics/touchgfx-open-repository
*
* @note This version of a lens, will not draw it self correctly if solid
* widgets are placed on top of it. This is due to the fact that
* the lens uses data from the frame buffer and the data
* below the lens (and the solid widgets) will not be updated in this case
*
* @sa touchgfx::Widget
*/
class Lens : public touchgfx::Widget
{
public:
/**
* @fn Lens::Lens();
*
* @brief initializes the lens data
*/
Lens();

/**
* @fn virtual touchgfx::Rect Lens::getSolidRect() const;
*
* @brief Report the solid dimensions of this widget.
* As we would like the frame buffer below to be drawn,
* the widget is reported as transparent
*
* @return an empty rectangle.
*/
virtual touchgfx::Rect getSolidRect() const;

/**
* @fn virtual void Lens::draw(const touchgfx::Rect& invalidatedArea) const;
*
* @brief read, distort and write the frame buffer contents.
*
* @param invalidatedArea The part of the widget that should be drawn.
*/
virtual void draw(const touchgfx::Rect& invalidatedArea) const;
private:
void apply(uint16_t* fb, int absx, int absy, int x, int y) const;

static const uint8_t lensRadius = 100;
static const uint8_t lensWidth = 2*lensRadius;
static const uint8_t lensZoom = 40;
int16_t lens[lensWidth][lensWidth]; //note: move the lens data to flash
touchgfx::Image lensImage;
};

#endif
27 changes: 27 additions & 0 deletions widgets/Lens/README.md
@@ -0,0 +1,27 @@
Purpose
========

![example](screenshots/lens-effect.png "Resulting lens effect")

The purpose of the Lens is to provide an effect on top of already existing graphics. The lens will distort the contents below it.

The lens has a radius of 100. This number can be adjusted.

The lens adds an image of a semi transparent glass on top of the distorted graphics. If the radius of the lens is adjusted, additional image data may be needed for the glass.

![glass](assets/lens_image.png "Glass")

Note that the lens is more dependent on the contents of the framebuffer and therefore the order of drawing than more regular widgets.

TouchGFX Version
=================

This widget was created and tested using TouchGFX version 4.2.0

Functional description
======================

Add the lens to a view.



Binary file added widgets/Lens/assets/background.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added widgets/Lens/assets/lens_image.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added widgets/Lens/screenshots/lens-effect.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 3be8493

Please sign in to comment.