Technique for drawing theme element (i.e. how to not stretch draw)? - windows

I'm looking at drawing a custom theme element onto a device content.
For example's sake, i will use the HeaderItem from the Windows XP header/listview:
(18×18 px)
Which we can blow up to see a little easier:
Note: I am not using the Theme API, nor am i asking about using the Theme API.
If i have my bitmap, like the one above, how can i draw it in practice?
Stretch draw ruins the style
The important problem that needs solving is how to maintain the important details. You can see the actual Windows XP Header draws the right-edge vertical line nice and crisp:
But if i were to blindly StretchBlt the image, the details become fuzzy:
The issue also happens with theme elements with crisp horizontal feature when the image is stretched vertically. In this case it also messes up the vertical gradient. But some other element have it even more pronounced.
So what is the technique that can be used to address this?
Should i cut 6 px off the top, left, bottom, and right?:
And then rather than drawing 1 image, i draw nine?:
And draw them with various horizontal or vertical stretch rules depending where it is?:
Unstretched
Horizontally stretched
Unstretched
Vertically stretched
Horizontally and vertically stretched
Vertically stretched
Unstretched
Horizontally stretched
Unstretched
This must be a solved problem already; since Windows already solved it, and who knows how many more Widget libraries that support themes.

Microsoft's solution to this problem can be reverse engineered by looking at the NormalBlue.ini file inside Luna.msstyles. Looking at the entry for Header.HeaderItem:
NormalBlue.ini:
[Header.HeaderItem]
bgtype = imagefile
SizingMargins = 8, 8, 3, 4
ContentMargins = 3, 0, 0, 0
ImageFile = Blue\ListViewHeader.bmp
imageCount=5
imageLayout=vertical
sizingType = tile
transparent=true
transparentcolor=255 0 0
FillColorHint = 250 248 243; Average fill color (light beige)
AccentColorHint = 252 194 71; Rollover hilite color (orange)
First we see it references the \Blue\ListViewHeader.bmp:
ImageFile = Blue\ListViewHeader.bmp
Which is:
And then there's the magic piece:
SizingMargins = 8, 8, 3, 4
This corresponds to TMT_SIZINGMARGINS:
TMT_SIZINGMARGINS: The margins used for sizing a non-true-size image.
where you can see some more hints in TmSchema.h:
//---- rendering MARGIN properties ----
TM_PROP(3601, TMT, SIZINGMARGINS, MARGINS) // margins used for 9-grid sizing
"Margins used for 9-grid sizing". This is a reference to the idea that you split up the image into 3x3 grid, and size the chunks independently as appropriate.
And the final piece is the documentation of the MARGINS type in UxTheme.h:
typedef struct _MARGINS
{
int cxLeftWidth; // width of left border that retains its size
int cxRightWidth; // width of right border that retains its size
int cyTopHeight; // height of top border that retains its size
int cyBottomHeight; // height of bottom border that retains its size
} MARGINS, *PMARGINS;
and its documentation:
cxLeftWidth: int - Width of the left border that retains its size.
cxRightWidth: int - Width of the right border that retains its size.
cyTopHeight: int - Height of the top border that retains its size.
cyBottomHeight: int - Height of the bottom border that retains its size.
Chop and Paint
The Luna theme is telling us that when we draw ListViewHeader.bmp, we need to use the sizing margins:
SizingMargins = 8, 8, 3, 4
And cut the image up into 9 pieces (3x3). But rather than using 6px all around (like i said in my question), we need to use the sizes that the designer of the image intended:
Left: 8
Right: 8
Top: 3
Bottom: 4
So given the 18×18 theme element image created in Photoshop by a designer:
The person who created the image said that my drawing code needs to cut off:
left 8 pixels
right 8 pixels
top 3 pixels
bottom 4 pixels
Meaning i then have to draw each of the nine images:
And then stretch draw some parts in certain directions:
Top-left: draw unstretched
Left: stretch vertically
Bottom-left: draw unstretched
Top: draw stretch horizontally
Middle: draw stretched horizontally and vertically
Bottom: draw stretched horizontally
Top-right: draw unstretched
Right: draw stretched vertically
Bottom-right: draw unstretched

Related

React Native Animated.event onScroll scale image from the top left corner rather center

Hello there actually I've an square image and I've managed that the image gets scaled to zero if I'm scrolling down.
My ScrollView onScroll
onScroll={Animated.event(
[{nativeEvent: {contentOffset:{y: this.state.scrollY}}}])}
My interpolate works if the users scroll 164px and reaches 209px then the image will be not visible.
let scale = this.state.scrollY.interpolate({inputRange: [164, 209], outputRange: [1, 0], extrapolate: "clamp"});
Here is my wrapper around the image with the transform (scale)
<Animated.View style={[styles.image, {opacity, transform: [{scale}]}]}>
Actually the Image is scaled from the center point rather from the top left corner, which I want to achieve. Do anyone knows how I can scale from the top left corner with the style property transform? I've uploaded a image to demonstrate.

Canvas compositing and cut-out effect

Using HTML5 Canvas, I basically have painted a colored rectangle. Now I am painting a black, wide stroke for the same rectangle but using the global composite operator Destination-In. My expected result was to have a half-width, red stroke and nothing else as painting the stroke via Destination-In should only paint the parts in the canvas where the red rectangle "hits" the stroke.. but the result is that the red rectangle's fill makes it through outside the stroke as well..
Did I missunderstand something? Here's the actual code in use:
var ctx2 = document.getElementById('canvas2').getContext('2d');
ctx2.fillStyle = "red";
ctx2.fillRect (10, 10, 200, 100);
ctx2.strokeStyle = "rgba(0,0,0,1)";
ctx2.lineWidth = 10;
ctx2.globalCompositeOperation='destination-in';
ctx2.strokeRect (10, 10, 200, 100);
EDIT: I've added a jsFiddle sample which shows what I'd expect / want and below the actual result I am getting:
http://jsfiddle.net/nLGct/1
The result of destination-in compositing will always be:
Some/none of your original red-filled rect will survive.
None of your new black stroke will ever survive.
Specifically:
The destination (the red rect) survives only where it's overlapped by the source (the black stroke).
All non-overlapping areas are made transparent.
Why you're getting your results:
In canvas, a stroke is always half-inside and half-outside its path.
Therefore, the inside-half of your black stroke overlaps the original red fill.
So what survives destination-in is the part of the original red fill where it overlaps the inside of the black stroke.
If your black stroke is 10px wide then the inner 5 pixels overlap the red filled rect and those red pixels will survive.

Core Graphics stroke width is inconsistent between lines & arcs?

The use case: I am subclassing UIView to create a custom view that "mattes" a UIImage with a rounded rectangle (clips the image to a rounded rect). The code is working; I've used a method similar to this question.
However, I want to stroke the clipping path to create a "frame". This works, but the arc strokes look markedly different than the line strokes. I've tried adjusting the stroke widths to greater values (I thought it was pixelation at first), but the anti-aliasing seems to handle arcs and lines differently.
Here's what I see on the simulator:
This is the code that draws it:
CGContextSetRGBStrokeColor(context, 0, 0, 0, STROKE_OPACITY);
CGContextSetLineWidth(context, 2.0f);
CGContextAddPath(context, roundRectPath);
CGContextStrokePath(context);
Anyone know how to make these line up smoothly?
… but the anti-aliasing seems to handle arcs and lines differently.
No, it doesn't.
Your stroke width is consistent—it's 2 pt all the way around.
What's wrong is that you have clipped to a rectangle, and your shape's sides are right on top of the edges of this rectangle, so only the halves of the sides that are inside the rectangle are getting drawn. That's why the edges appear only 1 px wide.
The solution is either not to clip, to grow your clipping rectangle by 2 pt on each axis before clipping to it, or to move your shape's edges inward by 1 pt on each side. (ETA: Or, yeah, do an inner stroke.)
Just in case anyone is trying to do the same thing I am (round rect an image):
The UIImageView class has a property layer, of type CALayer . CALayer already has this functionality built-in (it WAS a little surprising to me I couldn't find it anywhere):
UIImageView *thumbnailView = [UIImage imageNamed:#"foo.png"];
thumbnailView.layer.masksToBounds = YES;
thumbnailView.layer.cornerRadius = 15.0f;
thumbnailView.layer.borderWidth = 2.0f;
[self.view addSubview:thumbnailView];
Also does the trick.

Rotating a single image (but not the others) on an html5 canvas?

I have a sprite I'm animating on an html canvas using normal sprite sheet blitting. On certain key events I'd like to change the direction of the sprite (ie, flip it, or rotate it 180 degrees) without changing anything (the other sprites) on the canvas.
Does anyone know how to do this?
So I was having this issue with my game; I had cells for up, down, and left animations, but no right cells. So I needed to flip the left cells to draw the right cells.
For each sprite I keep track of it's current top and left in the canvas, as well as each cell's top and left in the sprite sheet.
I've seen previous answers showing a simple horizontal flip as just translating the origin and flipping (inverse scale) of the axes, BUT this does not take into account that with sprites, flipping the origin will mess up the sprite's registration point (its top and left on the canvas).
This issue manifested in the sprite being mirrored correctly, but it's position being off by the width of the sprite. I solved it by taking into account the width of the sprite. Notice how I'm using CanvasRenderingContext2D.prototype.drawImage with 9 arguments since I'm slicing a sprite out of a sprite sheet:
// check if we need to flip image (special case for right movement)
if(sprite.translated){
context.save();
context.translate(context.canvas.width, 0);
context.scale(-1, 1);
context.drawImage(sprite.sheet,
cell.left,
cell.top,
sprite.width,
sprite.height,
// secret sauce: change the destination's X registration point
context.canvas.width - sprite.left - sprite.width,
sprite.top,
sprite.width, sprite.height);
context.restore();
} else {
// Assumes cells are all the same width and height, set in sprite
context.drawImage(sprite.sheet, cell.left, cell.top, sprite.width,
sprite.height, sprite.left, sprite.top, sprite.width, sprite.height);
}
Note: I also could have done the math in the translate, since it's meant to simplify calculations elsewhere.
Simply redraw the sprite, with a rotate transformation. Transformations in HTML Canvas 2D Context
The canvas is just an off-screen buffer. It won't be cleared unless you tell it to, and nothing else will be changed unless you tell it to.
There's a bunch of different situations in which you may have to redraw the area of or around the sprite. Otherwise, you'll get a ghosting effect where part of the old sprite is still visible below the new drawing, or other drawings become obscured. Some reasons are:
Your sprite is partially transparent,
Your sprite is partially translucent,
Other drawings are made on top of your sprite,
Your sprite is non-rectangular,
You're doing flips that are not multiples of 90 degrees.
So that might be a bit more work, and there are several different approaches to doing that. You could simply redraw the entire scene, or just the specific objects at the location, perhaps using the clip method.
A completely different direction might be to use other HTML elements, img or div, with absolute positioning and CSS3 transformations. That's basically a bit of trickery to delegate the rendering of your scene to the browser.
While I appreciate Shtééf's answer, after a bit of research, I have found that rotating the canvas you are actually using to display doesn't seem to be ideal. The saving, rotating and restoring while trying to create complex animations (aka think Street Fighter 2 not astroids) causes the canvas to flicker in even Chrome.
I have found however a usable strategy. The idea here is that you actually create two canvases, one will be for your game and the other will be a backbuffer of sorts and it will be used to rotate or scale your sprites. You essentially transform the backbuffer canvas, draw the image in question, then transfer it to your main canvas and restore (or not) the backbuffer. In this manner, you only rotate the hidden canvas and only effect the sprite in question not the entire game board.
The code looks something like this (work in progress):
mainContext.clearRect(lastXpos, lastYpos, lastWidth, lastHeight);
backContext.clearRect(0, 0, lastWidth, lastHeight);
lastXpos = xpos;
lastYpos = ypos;
lastWidth = width;
lastHeight = height;
backContext.save();
//check the direction of the sprite
//turn the backContext to this direction
//SPRITE_INVERTED==-1
if (spriteXDirection == SPRITE_INVERTED || spriteYDirection == SPRITE_INVERTED)
{
var horScale = 0;
var verScale = 0;
if (spriteXDirection == SPRITE_INVERTED)
{
horScale = width;
}
if (spriteYDirection == SPRITE_INVERTED)
{
verScale = height;
}
backContext.translate(horScale, verScale);
backContext.scale(spriteXDirection, spriteYDirection);
}
//draw the sprite not we always use 0,0 for top/left
backContext.drawImage(animations[currentPlay].sheet,
animationX,
animationY,
width,
height, 0, 0, width, height);
//Get the image data from the back context
var image = backContext.getImageData(0, 0, width, height);
//flip the back context back to center - or not, I haven't decided how to optimize this yet.
backContext.restore();
//transfer the image to your main context
mainContext.putImageData(image, xpos, ypos);
This has saved me a lot of headaches in understanding how to translate my sprites without having everything on my gameboard move all over the place. It also seems to perform better then modifying the main context.
Why don't you use save() and restore
ctx.save(); // save current state
ctx.rotate(Math.PI); // rotate
ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
ctx.restore(); // restore original states (no rotation etc)
How to rotate one image in a canvas?

How to shift pixels of a pixmap efficient in Qt4

I have implemented a marquee text widget using Qt4. I painted the text content onto a pixmap first. And then paint a portion of this pixmap onto a paint device by calling painter.drawTiledPixmap(offsetX, offsetY, myPixmap)
My Imagination is that, Qt will fill the whole marquee text rectangle with the content from myPixmap.
Is there a ever faster way, to shift all existing content to left by 1px and than fill the newly exposed 1px wide and N-px high area with the content from myPixmap?
Well. This is a trick I used to do with slower hardware back in the old days. Basically, the image buffer is allocated twice as wide as needed with 1 extra line at the beginning. Build the image to the left of the buffer. Then draw the image repeatedly with the buffer advancing 1 pixel at a time in the buffer.
int w = 200;
int h = 100;
int rowBytes = w * sizeof(QRgb) * 2; // line buffer is twice as the width
QByteArray buffer(rowBytes * (h + 1), 0xFF); // 1 more line than the height
uchar * p = (uchar*)buffer.data() + rowBytes; // start drawing the image content at 2nd line
QImage image(p, w, h, rowBytes, QImage::Format_RGB32); // 1st line is used as the padding at the start of scroll
image.fill(qRgb(255, 0, 0)); // well. do something to the image
p = image.bits() - rowBytes / 2; // start scrolling at the middle of the 1st (blank) line
for(int i=0;i<w;++i, p+=sizeof(QRgb)) {
QImage scroll(p, w, h, rowBytes, QImage::Format_RGB32); // scrool 1 pixel at a time
scroll.save(QString("%1.png").arg(i));
}
I am not sure this will be any faster than just change the offset of the image and draw it strait. The hardware today is really powerful which renders a lot of old tricks useless. But it's fun to play obscure tricks. :)
Greetings,
one possibility to achieve this would be to:
Create a QGraphicsScene + View and put the pixmap on that twice (as QGraphicsPixmapItem), so they are right next to each other.
Size the view to fit the size of the (one) pixmap.
Then, instead of repainting the pixmap, you simply reposition the view's viewport, moving from one pixmap to the next.
Jump back at the end to create the loop.
This may or may not be faster (in terms of performance) - I have not tested it. But may be worth a try, if only for the sake of experiment.
Your approach is probably one of the fastest one since you use low level painting methods. You can implement an intermediate approach between low level painting and the QGraphicsScene option : using a scroll area containing a label.
Here is a sample of code that create a new scroll area containing a text label. You may scroll the label automatically using a QTimer to trigger the scrolling effect, that gives you a nice marquee widget.
QScrollArea *scrollArea = new QScrollArea();
// ensure that scroll bars never show
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QLabel *label = new QLabel("your scrolling text");
// resize the scroll area : 50px length and an height equals to its content height.
scrollArea->resize(50, label->size().height());
scrollArea->setWidget(label);
label->show(); // optionnal if the scroll area is not yet visible
The text label inside the scroll area can be moved from left to right by one pixel using the QScrollArea::scrollContentsBy(int dx, int dy) with a dx parameter equals to -1.
Why not just do it on a pixel by pixel basis? Due to the way caches work writing the pixel to the one before it all the way until you get to the end. Then you can fill the final column by reading from your other image.
Its then pretty easy to SIMD optimise it as well; though you start getting into per-platform optimisations at this point.

Resources