My goal is to define scale bars programmatically, that have a fixed length, but may be changed afterwards by the graphic designer.
I have come sofar as to define a closed path within the document:
def addLine(doc):
def point(x, y):
result = Dispatch("Photoshop.PathPointInfo")
result.Kind = 2 # for PsPointKind --> 2 (psCornerPoint)
result.LeftDirection = result.rightDirection = result.Anchor = (x, y)
return result
points = [
point(100, 100),
point(200, 100),
point(200, 110),
point(100, 110)
]
lineSubPathArray = Dispatch("Photoshop.SubPathInfo")
lineSubPathArray.Operation = 1 #for PsShapeOperation --> 1 (psShapeAdd
lineSubPathArray.Closed = True
lineSubPathArray.EntireSubPath = points
myPathItem = doc.PathItems.Add("bar-path", [lineSubPathArray])
From here, I can load the saved document back into photoshop (CS6) and then create a shape layer manually: Layer | New fill layer | solid color ...
This results in a shape layer, similar to what I get by using the line tool, in which the line effectively is a rectangle whose height may be changed later.
First question: how to create the fill layer by using the API?
Second: I defined a rectangle 100pixels wide, but I get one 418 pixels wide. The doc has its doc.application.preferences.rulerUnits set to psPixels (1). Why is this?
Last: Isn't it possible to define a line as true line defined by two end points and set its stroke width instead of it's height?
This may be of use:
You can define a new colour with new SolidColor()
// define fillColor
var fillColor = new SolidColor();
var myColour = [57, 181,74];
fillColor.rgb.red = myColour[0];
fillColor.rgb.green = myColour[1];
fillColor.rgb.blue = myColour[2];
and then fill your path by adding myPathItem.fillPath(fillColor,ColorBlendMode.NORMAL,100,false,0,true,true);
// Switch off any dialog boxes
displayDialogs = DialogModes.NO; // OFF
var originalUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// call the source document
var srcDoc = app.activeDocument;
create_path("bar-path");
function create_path(linename)
{
var points = [
[100, 100],
[200, 100],
[200, 110],
[100, 110]
];
// create the array of PathPointInfo objects
var lineArray = new Array();
for (var i = 0; i < points.length; i++)
{
lineArray[i] = new PathPointInfo;
lineArray[i].kind = PointKind.CORNERPOINT;
lineArray[i].anchor = points[i];
lineArray[i].leftDirection = lineArray[i].anchor;
lineArray[i].rightDirection = lineArray[i].anchor;
}
// create a SubPathInfo object, which holds the line array in its entireSubPath property.
var lineSubPathArray = new Array();
lineSubPathArray.push(new SubPathInfo());
lineSubPathArray[0].operation = ShapeOperation.SHAPEXOR;
lineSubPathArray[0].closed = true;
lineSubPathArray[0].entireSubPath = lineArray;
//create the path item, passing subpath to add method
var myPathItem = srcDoc.pathItems.add(linename, lineSubPathArray);
// define fillColor
var fillColor = new SolidColor();
var myColour = [57, 181,74];
fillColor.rgb.red = myColour[0];
fillColor.rgb.green = myColour[1];
fillColor.rgb.blue = myColour[2];
//fill the path so we can see something also
myPathItem.fillPath(fillColor,ColorBlendMode.NORMAL,100,false,0,true,true);
// deselect path
deselect_path();
}
// switch back to normal
app.preferences.rulerUnits = originalUnits;
// Set Display Dialogs back to normal
displayDialogs = DialogModes.ALL; // NORMAL
function deselect_path()
{
// =======================================================
var idslct = charIDToTypeID( "slct" );
var desc76 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref63 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref63.putEnumerated( idPath, idOrdn, idTrgt );
desc76.putReference( idnull, ref63 );
var idselectionModifier = stringIDToTypeID( "selectionModifier" );
var idselectionModifierType = stringIDToTypeID( "selectionModifierType" );
var idremoveFromSelection = stringIDToTypeID( "removeFromSelection" );
desc76.putEnumerated( idselectionModifier, idselectionModifierType, idremoveFromSelection );
executeAction( idslct, desc76, DialogModes.NO );
}
As for rectangle being too large: What resolution and units do you have the psd file set to? The script below will switch to pixels, and set the resolution of your document to 72dpi
As for doing a stroke to replace the line. Well... you've got options.
You can do a brush stroke with a two point line:
var idStrk = charIDToTypeID( "Strk" );
var desc105 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref35 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref35.putEnumerated( idPath, idOrdn, idTrgt );
desc105.putReference( idnull, ref35 );
var idUsng = charIDToTypeID( "Usng" );
var idPbTl = charIDToTypeID( "PbTl" );
desc105.putClass( idUsng, idPbTl );
executeAction( idStrk, desc105, DialogModes.NO );
However, I don't think that's what your after. You can turn your path into a shape and then have a stroke and fill colour assigned. However... you need a minimum of 3 points for that to work.
As a suggestion, instead of a path create a shape from start - which oddly will work with a minimum of two points. But I have no idea how to do that in code!
There are two lessons I have learned form translating the solution by Ghoul Fool into Python:
using COM in python, one can make a lot of mistakes that go unnoticed, otherwise than cryptic error messages far beside the point. Thies include syntactic errors such as missing/redundant parentheses and wrong capitalisation.
solutions that work with COM in python do not work the same with the Python-Photoshop-API and vice versa, even though, following the source code of the library, apparently exactly the same thing is happening. I have not always been able to divine solutions that work both ways.
Here is, for those who have come to this question later, what I have come to to get a shape layer with a bar that can be changed in height afterwards. The action that is called is nothing more than a recording of New Fill Layer from the layer-menu (CS6):
def makeBar(doc):
app = doc.parent
app.preferences.rulerUnits = Units.Pixels
def point(x, y):
result = Dispatch("Photoshop.PathPointInfo")
result.Kind = PointKind.CornerPoint
result.LeftDirection = result.rightDirection = result.Anchor = (x, y)
return result
points = [
point(100, 100),
point(200, 100),
point(200, 110),
point(100, 110)
]
lineSubPathArray = Dispatch("Photoshop.SubPathInfo")
lineSubPathArray.Operation = ShapeOperation.ShapeAdd
lineSubPathArray.Closed = True
lineSubPathArray.EntireSubPath = points
doc.PathItems.Add("bar-path", [lineSubPathArray])
app = Dispatch("Photoshop.Application")
doc = app.Open(self.fileName)
As for my second question: The unit of the path's cornerpoints are invariantly in Points, no matter what he rulers are set to.
Related
I'm trying to create an interactive filter in Bokeh for a graph representation
The source data come from a graph. The function SpotlightData(G) create the pandas dataframes with node and edge informations.
I need to pass the value from a range slider to the CDSView.
I try using js_on_change with a callback function (but I'm not a JS programmer).
Basically I want to create an array of bool values (evaluating a logical expression) and pass it to the view filter.
This is the code:
mytool = "pan , wheel_zoom , undo , reset"
plot = figure(title="Graph layout demonstration"
, tools=mytool
, width=800
, height=600
, background_fill_color='#efefef'
, toolbar_location = "below")
plot.axis.visible = False
plot.grid.grid_line_color = None
NodeDF , EdgeDF = SpotlightData(Spotlight)
Saldo_maxscale = math.ceil((NodeDF["Saldo"].abs().quantile(q=0.9))/1000)*1000
NodeSource = ColumnDataSource(NodeDF)
EdgeSource = ColumnDataSource(EdgeDF)
BF= BooleanFilter([True]*len(EdgeDF))
NodeView = CDSView(source=NodeSource ,filters=[])
EdgeView = CDSView(source=EdgeSource ,filters=[BF])
days_slider = RangeSlider(title="Dates",start=1,end=31,step=1,value=(1, 31))
callback = CustomJS(args=dict(source=EdgeDF["dt_ref_day"],boolout=BF.booleans), code="""
const data = source;
const s = cb_obj.value[0];
const e = cb_obj.value[1];
for (let i = 0; i < data.length; i++) {
boolout[i] = data[i]>=s && data[i]<=e
}
boolout.change.emit();
""")
days_slider.js_on_change('value', callback)
graph = GraphRenderer()
graph.layout_provider = StaticLayoutProvider(graph_layout=nx.spring_layout(Spotlight))
graph.node_renderer.data_source = NodeSource
graph.edge_renderer.data_source = EdgeSource
graph.node_renderer.view = NodeView
graph.edge_renderer.view = EdgeView
NodeColor = LinearColorMapper(palette = cc.CET_D3, low=(Saldo_maxscale*-1), high=Saldo_maxscale)
graph.node_renderer.glyph = Circle(size="LogSize",fill_color=transform('Saldo', NodeColor))
graph.edge_renderer.glyph = MultiLine(line_alpha=0.5, line_width="LogSize")
plot.renderers.append(graph)
show(column(days_slider, plot))
But nothing change in the visualization despite moving the slider.
Thank you all I'm quite a noob with Bokeh
PS: There is a constraint: In this case I can't run a bokeh server
I want to convert a point from a rectangle area to a mapping rectangle area which convert point is in the same scale.
For instance, the samll area has 4 points from (mx0, my0) to (mx3, my3), and the big area is from (x0, y0) to (x3, y3) .
Now that can get the input (intput_x, intput_y) to get the output (x,y) as the follow:
input (100, 100), get output (0, 0).
input (150, 100), get output (1920, 0).
input (100, 150), get output (0, 1080).
input (150, 150), get output (19200, 1080).
Assume the intput (intput_x, intput_y) = (110, 120), and the output (x, y) can get (384, 432).
The described as the diagram: click me.
Here is the python code:
import numpy as np
def interp(x, xp, fp, is_loop_test=True):
if (is_loop_test==True):
for i in range(100, 151):
bb = np.interp(i, xp, fp)
print(f'{i}, bb: {bb}')
return bb
else:
return np.interp(x, xp, fp)
if __name__ == '__main__':
in_x, in_y = 110, 120
xp = (100, 150)
fp = (0, 1920)
yp = (100, 150)
fyp = (0, 1080)
x, y = interp(in_x, xp, fp, False), interp(in_y, yp, fyp, False)
print(f'(x, y): ({x}, {y})')
I can using numpy interp() function to get the output with Python. However, I want to implement the same thing with Java without using numpy.
I have found the similar liner interp function named evaluate() in Java, I calculated the input x: 110, and the output was 251.40625.
It's not my expect value 384. I have no idea where is wrong.
Here's the Jave code:
public class Interp{
public static void main(String[] args){
float fraction = 0.078125f; // 150/1920=0.078125f
float startValue = 110.0f; // intput x
float endValue = 1920.0f; // 0 to 1920
float aa = evaluate(fraction, startValue, endValue);
System.out.println("x: " + aa);
}
public static Float evaluate(float fraction, Number startValue, Number endValue) {
float startFloat = startValue.floatValue();
return startFloat + fraction * (endValue.floatValue() - startFloat);
}
}
Could you help me or give me some idea to solve this problem? Thanks a lot!
you can use library like apache commons math to do same thing as numpy.
It is not recommended to implement it by yourself, it is difficult to do better.
Class org.apache.commons.math3.analysis.interpolation.LinearInterpolator meets your needs.
Code Example:
import org.apache.commons.math3.analysis.interpolation.LinearInterpolator;
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
// Test on commons-math3 version 3.6.1
public class InterpolatorTest {
public static void main(String[] args){
// will output 384.0
System.out.println(interpolator(110f, new double[]{100d, 150d}, new double[]{0d, 1920d}));
// will output 432.0
System.out.println(interpolator(120f, new double[]{100d, 150d}, new double[]{0d, 1080d}));
}
public static double interpolator(double value, double[] x, double[] y) {
LinearInterpolator interpolator = new LinearInterpolator();
PolynomialSplineFunction function = interpolator.interpolate(x, y);
return function.value(value);
}
}
Thanks for you help. I think I can solve it!
Here is my solution :D
Python version:
def interp(input_vlaue, small_area, big_area):
small_area_x = small_area[2][0] - small_area[0][0]
big_area_x = big_area[2][0] - big_area[0][0]
small_area_y = small_area[2][1] - small_area[0][1]
big_area_y = big_area[2][1] - big_area[0][1]
out_x = (big_area_x / small_area_x) * (input_vlaue[0] - small_area[0][0])
out_y = (big_area_y / small_area_y) * (input_vlaue[1] - small_area[0][1])
output = [out_x, out_y]
return output
if __name__ == '__main__':
intput_poiint = [110, 120]
small_area = [(100, 100), (150, 100), (150, 150), (100, 150)]
big_area = [(0, 0), (1920, 0), (1920, 1080), (0, 1080)]
convert_output = interp(input_vlaue=intput_poiint, small_area=small_area, big_area=big_area)
print(f'\nintput_poiint: {intput_poiint}')
print(f'\nconvert_output: {convert_output}')
Java version:
public class Interp{
public static void main(String[] args){
float [] intput_value = new float[2];
float [] small_area_left_up_point = new float[2];
float [] small_area_right_down_point = new float[2];
float [] big_area_left_up_point = new float[2];
float [] big_area_right_down_point = new float[2];
float [] fraction_small = new float[2];
float [] fraction_big = new float[2];
float [] output = new float[2];
intput_value[0] = (float) 110;
intput_value[1] = (float) 120;
small_area_left_up_point[0] = (float) 100;
small_area_left_up_point[1] = (float) 100;
small_area_right_down_point[0] = (float) 150;
small_area_right_down_point[1] = (float) 150;
big_area_left_up_point[0] = (float) 0;
big_area_left_up_point[1] = (float) 0;
big_area_right_down_point[0] = (float) 1920;
big_area_right_down_point[1] = (float) 1080;
fraction_small[0] = small_area_right_down_point[0] - small_area_left_up_point[0];
fraction_small[1] = small_area_right_down_point[1] - small_area_left_up_point[1];
fraction_big[0] = big_area_right_down_point[0] - big_area_left_up_point[0];
fraction_big[1] = big_area_right_down_point[1] - big_area_left_up_point[1];
output[0] = interp(intput_value[0], fraction_small[0], fraction_big[0], small_area_left_up_point[0]);
output[1] = interp(intput_value[1], fraction_small[1], fraction_big[1], small_area_left_up_point[1]);
System.out.println("(intput_x, intput_y): " + intput_value[0] +", "+ intput_value[1]);
System.out.println("(output_x, output_y): " + output[0] +", "+ output[1]);
}
public static Float interp(float input_value, float small_fraction, float big_fraction, float mapping_left_point){
return (big_fraction/small_fraction) * (input_value-mapping_left_point);
}
}
Both version input (110, 120) can get the output (384, 432).
I am pleased to convert it! :D
I try to make a dashboard with bokeh but my hover tool got broken, since I tried to plot the data in a single diagram. I found some answers to this problem, but unfortunately I can't adapt it to my code.
I want to be able to turn off and on the visibility of data.
(Another problem is, that the whole plot vanishes, if you change the daterange to an area, where no values exist, maybe someone has a workaround for this. In an older version I tried to only change source emit, when range is on data, but this leads to other problems. Maybe someone has an idea for that too.)
Here is a reduced minimal working version of my code:
import pandas as pd
import numpy as np
from datetime import datetime
from bokeh.models import Button, CheckboxGroup, ColumnDataSource, CustomJS, DatetimeTickFormatter, HoverTool
from bokeh.models.widgets import DateRangeSlider
from bokeh.layouts import layout, column, row
from bokeh.palettes import Category20
from bokeh.plotting import figure, output_file, show
datesX = pd.date_range(start='1/1/2018', periods=100) #incl. '2018-01-01' to '2018-04-10'
numbof = 3
datesX = datesX[0:10].union(datesX[20:100])
valuesY = pd.DataFrame(np.random.randint(0,25,size=(90, numbof)), columns=list((f'V{i}' for i in range(numbof))))
movingY = pd.DataFrame(np.random.randint(0,10,size=(90, numbof)), columns=list((f'M{i}' for i in range(numbof))))
preCDS ={}
for i in range(numbof):
preCDS.update({f'x{i}': datesX})
preCDS.update({f'y{i}': valuesY[f'V{i}']})
preCDS.update({f'm{i}': movingY[f'M{i}']})
source = ColumnDataSource(preCDS)
source2 = ColumnDataSource(preCDS)
# output to static HTML file
output_file('file.html')
# create a new plot with a title and axis labels
p = figure(
title='file1', x_axis_label='Date', y_axis_label='yValue',
y_range=(0, 30), x_axis_type='datetime',
tools="pan, wheel_zoom, box_zoom, reset, save",
plot_width=1800, plot_height=480)
ypsilon = [f'y{i}' for i in range(numbof)]
em = [f'm{i}' for i in range(numbof)]
hover = HoverTool(line_policy='nearest', names=ypsilon + em,
tooltips=[('Timestamp', '#x{%Y-%m-%d %H:%M:%S}'), ('Wert', '#y')], ###what to do with #x and #y to make it iterable: x0, x1, x2, y0, ...
formatters={'#x': 'datetime'})
p.add_tools(hover)
date_range_slider = DateRangeSlider(
title="DateRange", start=datesX[0], end=datesX[89],
value=(datesX[0], datesX[89]), width=800, bar_color = (32,120,180))
checkbox_vals = CheckboxGroup(labels=[f'Plot {i+1}' for i in range(numbof)], active=[i for i in range(numbof)])
checkbox_mova = CheckboxGroup(labels=[f'Plot {i+1}' for i in range(numbof)], active=[i for i in range(numbof)])
vals=[]
mova=[]
for i in range(numbof):
vals.append(p.circle(x=f'x{i}', y=f'y{i}', source=source, color = Category20[20][2*i], size=3, name=f'y{i}'))
mova.append(p.line(x=f'x{i}', y=f'm{i}', source=source, line_color = Category20[20][2*i+1], line_width=2, name=f'm{i}'))
rangeCallback = CustomJS(args=dict(source=source, ref_source=source2, dRs=date_range_slider), code="""
// print out array of date from, date to
//console.log(dRs.value);
// dates returned from slider are not at round intervals and include time
const date_from = Date.parse(new Date(dRs.value[0]).toDateString());
const date_to = Date.parse(new Date(dRs.value[1]).toDateString());
//console.log(date_from, date_to)
// Creating the Data Sources
const data = source.data;
const ref = ref_source.data;
for (const [key, value] of Object.entries(data)) {
console.log(key)
// Creating new Array and appending correctly parsed dates
if(key.indexOf("x") == 0){
let new_ref = [];
let from_pos = [];
let to_pos = [];
ref[key].forEach(elem => {
elem = Date.parse(new Date(elem).toDateString());
new_ref.push(elem);
})
// Creating Indices with new Array
from_pos[key] = new_ref.indexOf(date_from);
to_pos[key] = new_ref.indexOf(date_to) + 1;
// re-create the source data from "reference"
const dataKeyM = key.replace('x', 'm');
const dataKeyY = key.replace('x', 'y');
data[dataKeyM] = ref[dataKeyM].slice(from_pos[key], to_pos[key]);
data[key] = ref[key].slice(from_pos[key], to_pos[key]);
data[dataKeyY] = ref[dataKeyY].slice(from_pos[key], to_pos[key]);
}
}
/*if (new_ref.includes(date_from) && new_ref.includes(date_to)) {
source.change.emit();
}*/
source.change.emit();
""")
checkboxValsCallback = CustomJS(args=dict(vals=vals, checkboxVals=checkbox_vals), code="""
var indexOf = [].indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item)
return i;
}
return -1;
};
//vals.visible = indexOf.call(checkboxVals.active,0)>=0;
for (let i=0;i<vals.length;i++) {
vals[i].visible = indexOf.call(checkboxVals.active,i)>=0;
}
""")
checkboxMovaCallback = CustomJS(args=dict(mova=mova, checkboxMova=checkbox_mova), code="""
var indexOf = [].indexOf || function(item) {
for (var i = 0, l = this.length; i < l; i++) {
if (i in this && this[i] === item)
return i;
}
return -1;
};
//mova.visible = indexOf.call(checkboxMova.active,0)>=0;
for (let i=0;i<mova.length;i++) {
mova[i].visible = indexOf.call(checkboxMova.active,i)>=0;
}
""")
checkbox_vals.js_on_change('active', checkboxValsCallback)
checkbox_mova.js_on_change('active', checkboxMovaCallback)
date_range_slider.js_on_change('value', rangeCallback)
b1 = Button(label="select all", max_width = 300, button_type="primary")
b1.js_on_click(CustomJS(args=dict(s=checkbox_vals, t=checkbox_mova), code="""
s.active = [0,1,2]
t.active = [0,1,2]
"""))
b2 = Button(label="unselect all", max_width = 300)
b2.js_on_click(CustomJS(args=dict(s=checkbox_vals, t=checkbox_mova), code="""
s.active = []
t.active = []
"""))
layout = column(p, row(checkbox_vals, checkbox_mova, date_range_slider), row(b1, b2))
show(layout)
Thank You in advance.
For the tooltips: you specified x and y for the hover but when you look at the datasource of e.g. your line glyphs stored in mova you will see there is no x or y but many different fields. I changed the tooltips to x1 and y0 but I am not sure if this is wat you want. Just put a breakpoint somewhere and look e.g. in the mova[0].data_source.data and you will see all the available fields and choose the ones you want for your tooltips.
hover = HoverTool(line_policy='nearest', names=ypsilon + em,
tooltips=[('Timestamp', '#x1{%Y-%m-%d %H:%M:%S}'), ('Wert', '#y0')],
formatters={'#x1': 'datetime'})
I'm following the code at this link (also found below). My final task is convert raster image into vector data.
But When Executing the code following error is occurred:
Cannot use 'in' operator to search for 'type' in table3
I'm not quite sure about what is causing the issue here. Any help would be appreciated.
var count = sorted.size();
print(count)
var ndwi1 = sorted.map(
function(img) {
var img1 = img.clip(table2)
var ndwi = img1.normalizedDifference(['B3','B5']);
var thres = ndwi.gt(0.15).rename('thres');
var slop = ee.Terrain.slope(elevation.clip(table2));
var slope1 = slop.lt(20).rename('slope1');
var slope_m = slope1.updateMask(slope1);
//var and1 = ee.Image.and(thres,shadow);
var shadowMap=ee.Terrain.hillshade(elevation.clip(table2), img.get('SUN_AZIMUTH'),img.get('SUN_ELEVATION'));
var shadow = shadowMap.lt(255).rename('shadow');
var evi1 = thres.expression('(thr>0) && (shd >0) && (slp>0)? 1 : 0' ,
{
'thr' : thres,
'shd' : shadow,
'slp' : slope1
});
var evi = evi1.clip(table2);
var vectors = evi.addBands(evi).reduceToVectors({
reducer: ee.Reducer.count(),
geometry: 'table3',
crs : evi.projection(),
scale: 1000,
});
return vectors;
})
Map.addLayer(ndwi1)
I'm attempting to take and save a screenshot using just ctypes. I'm not terrifically familiar with the Windows API, so I've been leaning pretty heavily on the various example in the Microsoft Developer Network.
I've been using the following code from the MSDN as a template to try to recreate with ctypes.
void SaveBitmap(char *szFilename,HBITMAP hBitmap)
{
HDC hdc=NULL;
FILE* fp=NULL;
LPVOID pBuf=NULL;
BITMAPINFO bmpInfo;
BITMAPFILEHEADER bmpFileHeader;
do{
hdc=GetDC(NULL);
ZeroMemory(&bmpInfo,sizeof(BITMAPINFO));
bmpInfo.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
GetDIBits(hdc,hBitmap,0,0,NULL,&bmpInfo,DIB_RGB_COLORS);
if(bmpInfo.bmiHeader.biSizeImage<=0)
bmpInfo.bmiHeader.biSizeImage=bmpInfo.bmiHeader.biWidth*abs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount+7)/8;
if((pBuf = malloc(bmpInfo.bmiHeader.biSizeImage))==NULL)
{
//MessageBox( NULL, "Unable to Allocate Bitmap Memory",
break;
}
bmpInfo.bmiHeader.biCompression=BI_RGB;
GetDIBits(hdc,hBitmap,0,bmpInfo.bmiHeader.biHeight,pBuf, &bmpInfo,
DIB_RGB_COLORS);
if((fp = fopen(szFilename,"wb"))==NULL)
{
//MessageBox( NULL, "Unable to Create Bitmap File", "Error",
MB_OK|MB_ICONERROR);
break;
}
bmpFileHeader.bfReserved1=0;
bmpFileHeader.bfReserved2=0;
bmpFileHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+bmpInfo.bmiHeader.biSizeImage;
bmpFileHeader.bfType='MB';
bmpFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
fwrite(&bmpFileHeader,sizeof(BITMAPFILEHEADER),1,fp);
fwrite(&bmpInfo.bmiHeader,sizeof(BITMAPINFOHEADER),1,fp);
fwrite(pBuf,bmpInfo.bmiHeader.biSizeImage,1,fp);
}while(false);
if(hdc) ReleaseDC(NULL,hdc);
if(pBuf) free(pBuf);
if(fp) fclose(fp);
However, I'm having terrible luck getting the GetDIBits function to succeed. I'm moderately confident that I've got the actual screengrab code correct, but the save code is giving me a lot of trouble. And frustratingly, I'm not sure why these functions are failing.
My Python code:
libc = ctypes.cdll.msvcrt
# handle to the entire desktop Window
hdc = windll.user32.GetDC(None)
# DC for the entire window
h_dest = windll.gdi32.CreateCompatibleDC(hdc)
width = windll.user32.GetSystemMetrics(SM_CXSCREEN)
height = windll.user32.GetSystemMetrics(SM_CYSCREEN)
hb_desktop = windll.gdi32.CreateCompatibleBitmap(hdc, width, height)
windll.gdi32.SelectObject(h_dest, hb_desktop)
print windll.gdi32.BitBlt(h_dest, 0,0, width, height, hdc, 0, 0, 'SRCCOPY')
# Save Section
# ==============
bmp_info = BITMAPINFO()
bmp_header = BITMAPFILEHEADER()
hdc = windll.user32.GetDC(None)
bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER)
DIB_RGB_COLORS = 0
print windll.gdi32.GetDIBits(hdc, hCaptureBitmap, 0,0, None, byref(bmp_info), DIB_RGB_COLORS)
bmp_info.bmiHeader.biSizeImage = bmp_info.bmiHeader.biWidth *abs(bmp_info.bmiHeader.biHeight) * (bmp_info.bmiHeader.biBitCount+7)/8;
pBuf = (c_char * bmp_info.bmiHeader.biSizeImage)()
BI_RGB = 0
bmp_info.bmiHeader.biCompression = BI_RGB
print windll.gdi32.GetDIBits(hdc, hCaptureBitmap, 0, bmp_info.bmiHeader.biHeight, pBuf, byref(bmp_info), 0x00)
bmp_header.bfReserved1 = 0
bmp_header.bfReserved2 = 0
bmp_header.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bmp_info.bmiHeader.biSizeImage
bmp_header.bfType = 0x4D42
bmp_header.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
fp = libc.fopen('test.bmp',"wb")
libc.fwrite(byref(bmp_header), sizeof(BITMAPFILEHEADER), 1, fp)
libc.fwrite(byref(bmp_info.bmiHeader),
sizeof(BITMAPFILEHEADER), 1, fp)
libc.fwrite(pBuf, bmp_info.bmiHeader.biSizeImage, 1, fp)
I've added print statements to each GetDIBits call, and sure enough, they're all returning a failed code. I'm completely stumped here.