Retrieving a CGRect from a transformed CGContext and apply to a UIView - cgcontext

I am drawing a PDF page into a CGContext.
In order to properly draw it, I am applying some transformations to the context.
The pdf page rendered rect is smaller than the view's rect.
I want to create a third view that has exact same frame as the part of the view that has a pdf rendered.
My solution works, but not entirely. Sometimes (a lot of times) the rect is wrong.
This is what I am doing:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context {
CGContextSaveGState(context);
// apply transforms to context
// draw pdf page
CGRect calculatedFromRect = CGRectApplyAffineTransform(pageRect, CGContextGetCTM(context));
CGContextRestoreGState(context);
// now draw a green rect to test the frame on a not transformed context
GContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextFillRect(context, calculatedFromRect);
self.thirdView.frame = calculatedFromRect;
}
The thirdView is red. When both rects (view and drawing) are equal, I see a brown rect on the screen (red with alpha on top of the green rect). But sometimes I can see they two separated from each other (offset and size difference...when this happens, the thirdView.frame is bigger than calcularedRect).
Since all the involved views have the same size and coordinates, not converting the coordinates with convertRect:fromView: shouldn't be a problem. But I tried this and the result was the same.

Related

SwiftUI tint for image but preserving Black and White

I need to shift the color in an image, e.g. from gray to green. But only the parts that are not white and/or black...
For UIKit I had a handy extension:
// colorize image with given tint color
// this is similar to Photoshop's "Color" layer blend mode
// this is perfect for non-greyscale source images, and images that have both highlights and shadows that should be preserved
// white will stay white and black will stay black as the lightness of the image is preserved
func tint(tintColor: UIColor) -> UIImage {
return modifiedImage { context, rect in
// draw black background - workaround to preserve color of partially transparent pixels
context.setBlendMode(.normal)
UIColor.black.setFill()
context.fill(rect)
// draw original image
context.setBlendMode(.normal)
context.draw(self.cgImage!, in: rect)
// tint image (loosing alpha) - the luminosity of the original image is preserved
context.setBlendMode(.color)
tintColor.setFill()
context.fill(rect)
// mask by alpha values of original image
context.setBlendMode(.destinationIn)
context.draw(self.cgImage!, in: rect)
}
}
is there any way to generate the same functionality with the tint options in SwiftUI?
".colorMultiply" colors white as well.
".saturation(0.5)" directly generates a grayscale image.
You can continue to use your extension, like
var body: some View {
Image(uiImage: UIImage(named: "some")!.tint(tintColor: UIColor.red))
}
I was looking for the wrong keyword.
The actually way to do this in SwiftUI is hueRotation!
It only works with coloured images and not with grayscale images though.
See example below:
Color.blue
.hueRotation(.degrees(-45.0))

AndroidPlot - Labels and text

I am a non-developer product manager for an application built in both Android and iOS. We have a bar graph in iOS that provides text for the content of the graph. It displays Totals for each bar, and percentages for each segment of each bar.
In Android, using AndroidPlot (so I understand) we just display the bars with different color segments and no percent totals or totals. I am told by the developer that we can't show more.
I would display the images here, but stackoverflow tells me I don't have enough reputation points to do this. I have created a link to my dropbox with the images https://www.dropbox.com/sh/2uocm5bn79rerbe/AAB7s9QEEYIRIgXhKbUAaOyDa
Is it possible to use AndroidPlot to emulate this iOS chart or at least represent to same information to the end user?
Your developer is more or less correct but you have options. Androidplot's BarRenderer by default provides only an optional label at the top of each bar, which in your iOS images is occupied by the "available", "new", "used" and "rent" label. That label appears to be unused in your Android screenshot so one option would be to utilize those labels do display your totals.
As far as exactly matching the iOS implementation with Androidplot, the missing piece is the ability to add additional labels horizontally and vertically along the side of each bar. You can extend BarRenderer to do this by overriding it's onRender(...) method. Here's a link for your developer that shows where in the code he'll want to modify onRender(...).
I'd suggest these modifications to add the vertical labels:
Invoke Canvas.save(Canvas.ALL_SAVE_FLAG) to store the default orientation of the Canvas.
Use Canvas.translate(leftX, bottom) to center on the bottom left point of the bar
Rotate the Canvas 90 degrees using Canvas.rotate(90) to enable vertical text drawing
Draw whatever text is needed along the side of the plot; 0,0 now corresponds to the bottom left corner of the bar so start there when invoking canvas.drawText(x,y).
Invoke Canvas.restore() to restore the canvas' original orientation.
After implementing the above, adding horizontal "%" labels should be self evident but if you run into trouble feel free to ask more questions along the way.
UPDATE:
Here's a very basic implementation of the above. First the drawVerticalText method:
/**
*
* #param canvas
* #param paint paint used to draw the text
* #param text the text to be drawn
* #param x x-coord of where the text should be drawn
* #param y y-coord of where the text should be drawn
*/
protected void drawVerticalText(Canvas canvas, Paint paint, String text, float x, float y) {
// record the state of the canvas before the draw:
canvas.save(Canvas.ALL_SAVE_FLAG);
// center the canvas on our drawing coords:
canvas.translate(x, y);
// rotate into the desired "vertical" orientation:
canvas.rotate(-90);
// draw the text; note that we are drawing at 0, 0 and *not* x, y.
canvas.drawText(text, 0, 0, paint);
// restore the canvas state:
canvas.restore();
}
All that's left is to invoke this method where necessary. In your case it should be done once per BarGroup and should maintain a consistent position on the y axis. I added the following code to the STACKED case in BarRenderer.onRender(...), immediately above the break:
// needed some paint to draw with so I'll just create it here for now:
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(PixelUtils.spToPix(20));
drawVerticalText(
canvas,
paint,
"test",
barGroup.leftX,
basePositionY - PixelUtils.dpToPix(50)); // offset so the text doesnt intersect with the origin
Here's a screenshot of the result...sorry it's so huge:
Personally, I don't care for the fixed y-position of these vertical labels and would prefer them to float along the upper part of the bars. To accomplish this I modify my invocation of drawVerticalText(...) to look like this:
// needed some paint to draw with so I'll just create it here for now:
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(PixelUtils.spToPix(20));
// right-justify the text so it doesnt extend beyond the top of the bar
paint.setTextAlign(Paint.Align.RIGHT);
drawVerticalText(
canvas,
paint,
"test",
barGroup.leftX,
bottom);
Which produces this result:

Efficient way of scaling hundreds of QGraphicItems

I have a zoomable QGraphicsView with scene that contains hundreds (and even thousands) of datapoints. The points are represented by QGraphicsEllipseItems and collected into a QGraphicsItemGroup. When the view is zoomed in to I want datapoints to stay at a constant size (i.e., the distances between neigbouring points increase but the sizes stay the same). Right now I achieve this by running this code every time the user zooms in:
#get all the QGraphicsEllipseItems that make up the QGraphicsItemGroup
children = graphics_item_group.childItems()
for c in children:
#base_size_x and base_size_y are the sizes of the
#untrasformed ellipse (on the scene) when zoom factor is 1
#New width and height are obtained from the original sizes and
#the new zoom factors (h_scale, v_scale)
new_width = base_size_x/h_scale
new_height = base_size_y/v_scale
#The top-left corner of the new rectangle for the item has to be recalculated
#when scaling in order to keep the center at a constant position
#For this, the center of the item has to be stored first
old_center_x = c.rect().center().x()
old_center_y = c.rect().center().y()
#New coordinates of the rectangle top left point are calculated
new_topleft_x = old_center_x - new_width/2.
new_topleft_y = old_center_y - new_height/2.
#Finally a new rectangle is set for the ellipse
c.setRect(new_topleft_x, new_topleft_y, new_width, new_height)
This code works. The problem is that it is quite slow (without the compensatory scaling zooming in/out works very smoothly). I tried turning off antialiasing for the view but it makes things look pretty ugly. Is there anything else that I can do to make the processing/redrawing faster?
In the constructor of the QGraphicsItem:
setFlag(ItemIgnoresTransformations);
When you zoom, the item will remain the same size, and you won't have to manually scale it.

How to change bitmap image runtime based on condition and set on canvas?

I have nine rectangles drawn on canvas and I had set image on it through bitmap by decoding it from resources. I am generating random numbers between 1 to 10 and set it to text view.
When user moves to rectangle based on number displaying in text view the image of that particular rectangle should be changed
if(textview.getText().toString()==1)
{
canvas.drawBitmap(bitmapHolo,null,rect[i],null);
}
else
{
canvas.drawBitmap(bitmapRectangle,null,rect[i],null);
}

MonoTouch: How to resize a view.Frame inside Draw override and draw correctly?

the problem I am having it that if inside the UIView Draw override, I change the view frame size, drawing a rectangle is not working as expected.
If I change the view frame size outside of the Draw override, it works fine. Is this an expected behavior or is it a problem with monotouch only?
This is the code I am using:
class ChildView : UIView
{
public override void Draw (RectangleF rect)
{
base.Draw (rect);
CGContext g = UIGraphics.GetCurrentContext();
//adding 30 points to view height
RectangleF rec = new RectangleF(this.Frame.Location,this.Frame.Size);
rec.Height+=30;
RectangleF rec_bounds = new RectangleF(0,0,rec.Width,rec.Height);
this.Frame=rec;
this.Bounds=rec_bounds;
//drawing a red rectangle to the first half of view height
UIColor.Red.SetFill();
RectangleF _rect = new RectangleF(this.Bounds.Location,this.Bounds.Size);
_rect.Height=_rect.Height/2;
g.FillRect(_rect);
}
}
However, the output of this code is this: (it should draw only 30 points red, but it draws 60 points)
Here is a link to download the project to reproduce this issue:
www.grbytes.com\downloads\RectangleDrawProblem.rar
Καλημέρα!
This behavior is expected. If you want to change the view's frame inside the Draw override, do it before getting the current context. That is because the graphics context also has a size and that is the size of the view at the time you are retrieving it.
Also, there is no need to set both the Bounds and the Frame of the view. You can just set either of them in this case.
By the way, I don't think you need to call base.Draw(). According to the Apple documentation, "If you subclass UIView directly, your implementation of this method does not need to call super."

Resources