func cNewImageSurface(s *C.cairo_surface_t) (Surface, error) { format := Format(C.cairo_image_surface_get_format(s)) width := int(C.cairo_image_surface_get_width(s)) height := int(C.cairo_image_surface_get_height(s)) stride := int(C.cairo_image_surface_get_stride(s)) return newImg(s, format, width, height, stride) }
// pdf, ps, and svg surfaces use the current surface (page) func (g *Graphic) Image() (image.Image, error) { var surface *C.cairo_surface_t switch g.format { case "eps", "pdf", "ps", "svg": // map vector surfaces to an image surface surface = C.cairo_surface_map_to_image(g.surface, nil) defer C.cairo_surface_unmap_image(g.surface, surface) status := C.cairo_surface_status(surface) err := statusToError(status) if err != nil { return nil, err } case "png", "jpeg": // no conversion needed surface = g.surface } width := int(C.cairo_image_surface_get_width(surface)) height := int(C.cairo_image_surface_get_height(surface)) stride := int(C.cairo_image_surface_get_stride(surface)) format := C.cairo_image_surface_get_format(surface) dataPtr := C.cairo_image_surface_get_data(surface) data := C.GoBytes(unsafe.Pointer(dataPtr), C.int(stride*height)) var img image.Image switch format { case C.CAIRO_FORMAT_ARGB32: img = &extimage.ARGB{ Pix: data, Stride: stride, Rect: image.Rect(0, 0, width, height), } case C.CAIRO_FORMAT_RGB24: img = &extimage.RGB{ Pix: data, Stride: stride, Rect: image.Rect(0, 0, width, height), } case C.CAIRO_FORMAT_A8: img = &image.Alpha{ Pix: data, Stride: stride, Rect: image.Rect(0, 0, width, height), } default: // known unsupported formats: // CAIRO_FORMAT_INVALID = -1, // CAIRO_FORMAT_A1 = 3, // CAIRO_FORMAT_RGB16_565 = 4, // CAIRO_FORMAT_RGB30 = 5 panic(fmt.Sprintf("unsupported cairo image surface format: %d", int(format))) } return img, nil }
// GetData returns a copy of the surfaces raw pixel data. // This method also calls Flush. func (self *Surface) GetData() []byte { self.Flush() dataPtr := C.cairo_image_surface_get_data(self.surface) if dataPtr == nil { panic("cairo.Surface.GetData(): can't access surface pixel data") } stride := C.cairo_image_surface_get_stride(self.surface) height := C.cairo_image_surface_get_height(self.surface) return C.GoBytes(unsafe.Pointer(dataPtr), stride*height) }
//CreateSimilarImage creates a new surface that is as compatible as possible //for uploading to and using in conjunction with existing surface. //However, this surface can still be used like any normal image surface. // //Initially the contents of the returned surface are all 0 (transparent if contents //have transparency, black otherwise.) // //Originally cairo_surface_create_similar_image. func (e *XtensionSurface) CreateSimilarImage(f Format, w, h int) (ImageSurface, error) { s := C.cairo_surface_create_similar_image(e.s, f.c(), C.int(w), C.int(h)) stride := int(C.cairo_image_surface_get_stride(s)) o := ImageSurface{ XtensionSurface: NewXtensionSurface(s), format: f, width: w, height: h, stride: stride, } return o, o.Err() }
func (v *Surface) GetData() []byte { c_data := C.cairo_image_surface_get_data(v.native()) c_data_len := int(C.cairo_image_surface_get_stride(v.native()) * C.cairo_image_surface_get_height(v.native())) hdr := reflect.SliceHeader{ Data: uintptr(unsafe.Pointer(c_data)), Len: c_data_len, Cap: c_data_len, } goSlice := *(*[]byte)(unsafe.Pointer(&hdr)) return goSlice }
// GetSnapshotCustom runs asynchronously, taking a snapshot of the WebView. // Upon completion, resultCallback will be called with a copy of the underlying // bitmap backing store for the frame, or with an error encountered during // execution. // // See also: webkit_web_view_get_snapshot at // http://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-get-snapshot func (v *WebView) GetSnapshotCustom(region SnapshotRegion, options SnapshotOptions, resultCallback func(result *image.RGBA, err error)) { var cCallback C.GAsyncReadyCallback var userData C.gpointer var err error if resultCallback != nil { callback := func(result *C.GAsyncResult) { var snapErr *C.GError snapResult := C.webkit_web_view_get_snapshot_finish(v.webView, result, &snapErr) if snapResult == nil { defer C.g_error_free(snapErr) msg := C.GoString((*C.char)(snapErr.message)) resultCallback(nil, errors.New(msg)) return } defer C.cairo_surface_destroy(snapResult) if C.cairo_surface_get_type(snapResult) != cairoSurfaceTypeImage || C.cairo_image_surface_get_format(snapResult) != cairoImageSurfaceFormatARGB32 { panic("Snapshot in unexpected format") } w := int(C.cairo_image_surface_get_width(snapResult)) h := int(C.cairo_image_surface_get_height(snapResult)) stride := int(C.cairo_image_surface_get_stride(snapResult)) C.cairo_surface_flush(snapResult) data := unsafe.Pointer(C.cairo_image_surface_get_data(snapResult)) //(miha) fix endianes depended byte order, and copy to go slice at the same time. data_fixed := make([]byte, stride*h) C.gowk2_cairo_endian_depended_ARGB32_to_RGBA((*C.uchar)(data), (*C.uchar)(&data_fixed[0]), C.uint(stride*h)) rgba := &image.RGBA{data_fixed, stride, image.Rect(0, 0, w, h)} // slower but doesn't use Go pointers inside C. See https://github.com/golang/go/issues/8310 !!!!!!! //C.gowk2_cairo_endian_depended_ARGB32_to_RGBA((*C.uchar)(data), C.uint(stride*h)) //rgba := &image.RGBA{C.GoBytes(data, C.int(stride*h)), stride, image.Rect(0, 0, w, h)} resultCallback(rgba, nil) } cCallback, userData, err = newGAsyncReadyCallback(callback) if err != nil { panic(err) } } C.webkit_web_view_get_snapshot(v.webView, (C.WebKitSnapshotRegion)(region), (C.WebKitSnapshotOptions)(options), nil, cCallback, userData) }
// SetData sets the surfaces raw pixel data. // This method also calls Flush and MarkDirty. func (self *Surface) SetData(data []byte) { self.Flush() dataPtr := unsafe.Pointer(C.cairo_image_surface_get_data(self.surface)) if dataPtr == nil { panic("cairo.Surface.SetData(): can't access surface pixel data") } stride := C.cairo_image_surface_get_stride(self.surface) height := C.cairo_image_surface_get_height(self.surface) if len(data) != int(stride*height) { panic("cairo.Surface.SetData(): invalid data size") } C.memcpy(dataPtr, unsafe.Pointer(&data[0]), C.size_t(stride*height)) self.MarkDirty() }
// GetSnapshot runs asynchronously, taking a snapshot of the WebView. // Upon completion, resultCallback will be called with a copy of the underlying // bitmap backing store for the frame, or with an error encountered during // execution. // // See also: webkit_web_view_get_snapshot at // http://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-get-snapshot func (v *WebView) GetSnapshot(resultCallback func(result *image.RGBA, err error)) { var cCallback C.GAsyncReadyCallback var userData C.gpointer var err error if resultCallback != nil { callback := func(result *C.GAsyncResult) { var snapErr *C.GError snapResult := C.webkit_web_view_get_snapshot_finish(v.webView, result, &snapErr) if snapResult == nil { defer C.g_error_free(snapErr) msg := C.GoString((*C.char)(snapErr.message)) resultCallback(nil, errors.New(msg)) return } defer C.cairo_surface_destroy(snapResult) if C.cairo_surface_get_type(snapResult) != cairoSurfaceTypeImage || C.cairo_image_surface_get_format(snapResult) != cairoImageSurfaceFormatARB32 { panic("Snapshot in unexpected format") } w := int(C.cairo_image_surface_get_width(snapResult)) h := int(C.cairo_image_surface_get_height(snapResult)) stride := int(C.cairo_image_surface_get_stride(snapResult)) data := unsafe.Pointer(C.cairo_image_surface_get_data(snapResult)) rgba := &image.RGBA{C.GoBytes(data, C.int(stride*h)), stride, image.Rect(0, 0, w, h)} resultCallback(rgba, nil) } cCallback, userData, err = newGAsyncReadyCallback(callback) if err != nil { panic(err) } } C.webkit_web_view_get_snapshot(v.webView, (C.WebKitSnapshotRegion)(1), // FullDocument is the only working region at this point (C.WebKitSnapshotOptions)(0), nil, cCallback, userData) }
//export our_area_draw_callback func our_area_draw_callback(widget *C.GtkWidget, cr *C.cairo_t, data C.gpointer) C.gboolean { var x0, y0, x1, y1 C.double var maxwid, maxht C.gint s := (*sysData)(unsafe.Pointer(data)) // thanks to desrt in irc.gimp.net/#gtk+ // these are in user coordinates, which match what coordinates we want by default, even out of a draw event handler (thanks johncc3, mclasen, and Company in irc.gimp.net/#gtk+) C.cairo_clip_extents(cr, &x0, &y0, &x1, &y1) // we do not need to clear the cliprect; GtkDrawingArea did it for us beforehand cliprect := image.Rect(int(x0), int(y0), int(x1), int(y1)) // the cliprect can actually fall outside the size of the Area; clip it by intersecting the two rectangles C.gtk_widget_get_size_request(widget, &maxwid, &maxht) cliprect = image.Rect(0, 0, int(maxwid), int(maxht)).Intersect(cliprect) if cliprect.Empty() { // no intersection; nothing to paint return C.FALSE // signals handled without stopping the event chain (thanks to desrt again) } i := s.handler.Paint(cliprect) surface := C.cairo_image_surface_create( C.CAIRO_FORMAT_ARGB32, // alpha-premultiplied; native byte order C.int(i.Rect.Dx()), C.int(i.Rect.Dy())) if status := C.cairo_surface_status(surface); status != C.CAIRO_STATUS_SUCCESS { panic(fmt.Errorf("cairo_create_image_surface() failed: %s\n", C.GoString(C.cairo_status_to_string(status)))) } // the flush and mark_dirty calls are required; see the cairo docs and https://git.gnome.org/browse/gtk+/tree/gdk/gdkcairo.c#n232 (thanks desrt in irc.gimp.net/#gtk+) C.cairo_surface_flush(surface) toARGB(i, uintptr(unsafe.Pointer(C.cairo_image_surface_get_data(surface))), int(C.cairo_image_surface_get_stride(surface))) C.cairo_surface_mark_dirty(surface) C.cairo_set_source_surface(cr, surface, 0, 0) // origin of the surface // that just set the brush that cairo uses: we have to actually draw now // (via https://developer.gnome.org/gtkmm-tutorial/stable/sec-draw-images.html.en) C.cairo_rectangle(cr, x0, y0, x1, y1) // breaking the nrom here since we have the coordinates as a C double already C.cairo_fill(cr) C.cairo_surface_destroy(surface) // free surface return C.FALSE // signals handled without stopping the event chain (thanks to desrt again) }
func (i *imagelist) Append(img *image.RGBA) { var width, height C.gint surface := C.cairo_image_surface_create(C.CAIRO_FORMAT_ARGB32, C.int(img.Rect.Dx()), C.int(img.Rect.Dy())) if status := C.cairo_surface_status(surface); status != C.CAIRO_STATUS_SUCCESS { panic(fmt.Errorf("cairo_create_image_surface() failed in ImageList.Append(): %s\n", C.GoString(C.cairo_status_to_string(status)))) } C.cairo_surface_flush(surface) toARGB(img, uintptr(unsafe.Pointer(C.cairo_image_surface_get_data(surface))), int(C.cairo_image_surface_get_stride(surface)), false) // not NRGBA C.cairo_surface_mark_dirty(surface) basepixbuf := C.gdk_pixbuf_get_from_surface(surface, 0, 0, C.gint(img.Rect.Dx()), C.gint(img.Rect.Dy())) if basepixbuf == nil { panic(fmt.Errorf("gdk_pixbuf_get_from_surface() failed in ImageList.Append() (no reason available)")) } if C.gtk_icon_size_lookup(scaleTo, &width, &height) == C.FALSE { panic(fmt.Errorf("gtk_icon_size_lookup() failed in ImageList.Append() (no reason available)")) } if int(width) == img.Rect.Dx() && int(height) == img.Rect.Dy() { // just add the base pixbuf; we're good i.list = append(i.list, basepixbuf) C.cairo_surface_destroy(surface) return } // else scale pixbuf := C.gdk_pixbuf_scale_simple(basepixbuf, C.int(width), C.int(height), C.GDK_INTERP_NEAREST) if pixbuf == nil { panic(fmt.Errorf("gdk_pixbuf_scale_simple() failed in ImageList.Append() (no reason available)")) } i.list = append(i.list, pixbuf) C.gdk_pixbuf_unref(basepixbuf) C.cairo_surface_destroy(surface) }
func (self *Surface) GetStride() int { return int(C.cairo_image_surface_get_stride(self.surface)) }
// GetSnapshot runs asynchronously, taking a snapshot of the WebView. // Upon completion, resultCallback will be called with a copy of the underlying // bitmap backing store for the frame, or with an error encountered during // execution. // // See also: webkit_web_view_get_snapshot at // http://webkitgtk.org/reference/webkit2gtk/stable/WebKitWebView.html#webkit-web-view-get-snapshot func (v *WebView) GetSnapshot(resultCallback func(result *image.RGBA, err error)) { var cCallback C.GAsyncReadyCallback var userData C.gpointer var err error if resultCallback != nil { callback := func(result *C.GAsyncResult) { var snapErr *C.GError snapResult := C.webkit_web_view_get_snapshot_finish(v.webView, result, &snapErr) if snapResult == nil { defer C.g_error_free(snapErr) msg := C.GoString((*C.char)(snapErr.message)) resultCallback(nil, errors.New(msg)) return } defer C.cairo_surface_destroy(snapResult) if C.cairo_surface_get_type(snapResult) != cairoSurfaceTypeImage || C.cairo_image_surface_get_format(snapResult) != cairoImageSurfaceFormatARGB32 { panic("Snapshot in unexpected format") } w := int(C.cairo_image_surface_get_width(snapResult)) h := int(C.cairo_image_surface_get_height(snapResult)) stride := int(C.cairo_image_surface_get_stride(snapResult)) data := unsafe.Pointer(C.cairo_image_surface_get_data(snapResult)) surfaceBytes := C.GoBytes(data, C.int(stride*h)) // convert from b,g,r,a or a,r,g,b(local endianness) to r,g,b,a testint, _ := binary.ReadUvarint(bytes.NewBuffer([]byte{0x1, 0})) if testint == 0x1 { // Little: b,g,r,a -> r,g,b,a for i := 0; i < w*h; i++ { b := surfaceBytes[4*i+0] r := surfaceBytes[4*i+2] surfaceBytes[4*i+0] = r surfaceBytes[4*i+2] = b } } else { // Big: a,r,g,b -> r,g,b,a for i := 0; i < w*h; i++ { a := surfaceBytes[4*i+0] r := surfaceBytes[4*i+1] g := surfaceBytes[4*i+2] b := surfaceBytes[4*i+3] surfaceBytes[4*i+0] = r surfaceBytes[4*i+1] = g surfaceBytes[4*i+2] = b surfaceBytes[4*i+3] = a } } rgba := &image.RGBA{surfaceBytes, stride, image.Rect(0, 0, w, h)} resultCallback(rgba, nil) } cCallback, userData, err = newGAsyncReadyCallback(callback) if err != nil { panic(err) } } C.webkit_web_view_get_snapshot(v.webView, (C.WebKitSnapshotRegion)(1), // FullDocument is the only working region at this point (C.WebKitSnapshotOptions)(0), nil, cCallback, userData) }
//NewImageSurface creates an image surface of the given width, height, //and format. // //Originally cairo_image_surface_create. func NewImageSurface(format Format, width, height int) (ImageSurface, error) { is := C.cairo_image_surface_create(format.c(), C.int(width), C.int(height)) stride := int(C.cairo_image_surface_get_stride(is)) return newImg(is, format, width, height, stride) }
// Fill the image evaluating the function over each pixel // You can specify one function per channel, or one function for all the channels // The values will be normalized // BUG(akiross) this should return an error func (img *Image) FillMath(chanFuncs ...PixelFunc) { nch := len(chanFuncs) // BUG(akiross) this code introduces checks which may be unnecessary // in production, would be nice to disable this checking switch img.ColorSpace { case MODE_A8: if nch != 1 { fmt.Println("ERROR! For A8 images is mandatory to use a single chanFunc") return } case MODE_G8: if nch != 1 { fmt.Println("ERROR! For G8 images is mandatory to use a single chanFunc") return } case MODE_RGB: if nch < 1 || nch > 3 { fmt.Println("ERROR! For mode RGB you need 1, 2 or 3 chanFuncs") return } default: fmt.Println("RGBA not implemented yet! Alpha must evaluate to 1, or value is always transparent!") return /* if nch < 1 || nch > 4 { fmt.Println("ERROR! For mode RGBA you need from 1 to 4 chanFuncs") return } if nch == 4 { // If 4 parameters are specified, the alpha gets moved in front chanFuncs[0], chanFuncs[1], chanFuncs[2], chanFuncs[3] = chanFuncs[3], chanFuncs[0], chanFuncs[1], chanFuncs[2] } else { // Else, first parameter is alpha (nil), other parameters follow chanFuncs2 := []PixelFunc{nil} chanFuncs = append(chanFuncs2, chanFuncs...) } Problem: if alpha returns a constant value, or if it is nil, image is always transparent (normalized -> 0) */ } // Evaluate the channel functions in every point realData := make([][][]float64, nch) //realData := make([]float64, img.H*img.W*nch) max := make([]float64, nch) // Hold max value per each channel min := make([]float64, nch) // Hold min value per each channel for k := 0; k < nch; k++ { realData[k] = make([][]float64, img.H) // If the channel function is defined, use it on every pixel // othersize, the data, min and max are already set to 0 if chanFuncs[k] != nil { for i := 0; i < img.H; i++ { realData[k][i] = make([]float64, img.W) for j := 0; j < img.W; j++ { realData[k][i][j] = chanFuncs[k](j, i) max[k] = math.Max(max[k], realData[k][i][j]) min[k] = math.Min(min[k], realData[k][i][j]) } } } } // Copy the data onto the image stride := int(C.cairo_image_surface_get_stride(img.Surf)) // Stride in bytes rawData := unsafe.Pointer(C.cairo_image_surface_get_data(img.Surf)) // Prepare byte data, normalizing if necessary (we cannot write directly to unsafe.Pointer) byteData := make([]byte, stride*img.H) // Depending on format, we copy the data in different ways switch img.ColorSpace { case MODE_A8: fmt.Println("Copying data for mode A8") const k = 0 if max[k] != min[k] { for i := 0; i < img.H; i++ { for j := 0; j < img.W; j++ { byteData[i*stride+j] = byte(0xff * (realData[k][i][j] - min[k]) / (max[k] - min[k])) } } } case MODE_G8: fmt.Println("Copying data for mode G8") const k = 0 if max[k] != min[k] { for i := 0; i < img.H; i++ { for j := 0; j < img.W; j++ { p := i*stride + j*4 v := byte(0xff * (realData[k][i][j] - min[k]) / (max[k] - min[k])) byteData[p], byteData[p+1], byteData[p+2] = v, v, v } } } case MODE_RGB: fmt.Println("Copying data mode RGB") for k := 0; k < nch; k++ { if max[k] != min[k] { for i := 0; i < img.H; i++ { for j := 0; j < img.W; j++ { p := i*stride + j*4 + 1 byteData[p+k] = byte(0xff * (realData[k][i][j] - min[k]) / (max[k] - min[k])) } } } } /* case MODE_RGBA: fmt.Println("Copying data mode RGBA") for k := 0; k < nch; k++ { if max[k] != min[k] { for i := 0; i < img.H; i++ { for j := 0; j < img.W; j++ { p := i*stride + j*4 + 1 byteData[p+k] = byte(0xff * (realData[k][i][j] - min[k]) / (max[k] - min[k])) } } } } */ default: fmt.Println("ERROR! Not implemented yet") } // Copy the data on the C-side C.memcpy(rawData, unsafe.Pointer(&byteData[0]), C.size_t(stride*img.H)) }
//export go_genericGAsyncCallback func go_genericGAsyncCallback(source *C.GObject, result *C.GAsyncResult, callbackId *C.char) { key := C.GoString(callbackId) if obj, ok := cgoget(key); ok { switch obj.(type) { case *RunJavaScriptResponse: var jserr *C.GError response := obj.(*RunJavaScriptResponse) if response.Autoremove { defer cgounregister(key) } if jsResult := C.webkit_web_view_run_javascript_finish(response.CWebView, result, &jserr); jsResult == nil { defer C.g_error_free(jserr) msg := C.GoString((*C.char)(jserr.message)) response.Reply(nil, errors.New(msg)) } else { ctxRaw := gojs.RawGlobalContext(unsafe.Pointer(C.webkit_javascript_result_get_global_context(jsResult))) jsValRaw := gojs.RawValue(unsafe.Pointer(C.webkit_javascript_result_get_value(jsResult))) ctx := (*gojs.Context)(gojs.NewGlobalContextFrom(ctxRaw)) jsVal := ctx.NewValueFrom(jsValRaw) response.Reply(jsVal, nil) } case *GetSnapshotAsImageResponse: var snapErr *C.GError response := obj.(*GetSnapshotAsImageResponse) if response.Autoremove { defer cgounregister(key) } if snapResult := C.webkit_web_view_get_snapshot_finish(response.CWebView, result, &snapErr); snapResult == nil { defer C.g_error_free(snapErr) msg := C.GoString((*C.char)(snapErr.message)) response.Reply(nil, errors.New(msg)) } else { defer C.cairo_surface_destroy(snapResult) if C.cairo_surface_get_type(snapResult) != cairoSurfaceTypeImage || C.cairo_image_surface_get_format(snapResult) != cairoImageSurfaceFormatARGB32 { response.Reply(nil, errors.New("Snapshot in unexpected format")) return } w := int(C.cairo_image_surface_get_width(snapResult)) h := int(C.cairo_image_surface_get_height(snapResult)) stride := int(C.cairo_image_surface_get_stride(snapResult)) data := unsafe.Pointer(C.cairo_image_surface_get_data(snapResult)) surfaceBytes := C.GoBytes(data, C.int(stride*h)) // convert from b,g,r,a or a,r,g,b(local endianness) to r,g,b,a testint, _ := binary.ReadUvarint(bytes.NewBuffer([]byte{0x1, 0})) if testint == 0x1 { // Little: b,g,r,a -> r,g,b,a for i := 0; i < w*h; i++ { b := surfaceBytes[4*i+0] r := surfaceBytes[4*i+2] surfaceBytes[4*i+0] = r surfaceBytes[4*i+2] = b } } else { // Big: a,r,g,b -> r,g,b,a for i := 0; i < w*h; i++ { a := surfaceBytes[4*i+0] r := surfaceBytes[4*i+1] g := surfaceBytes[4*i+2] b := surfaceBytes[4*i+3] surfaceBytes[4*i+0] = r surfaceBytes[4*i+1] = g surfaceBytes[4*i+2] = b surfaceBytes[4*i+3] = a } } rgba := &image.RGBA{ Pix: surfaceBytes, Stride: stride, Rect: image.Rect(0, 0, w, h), } response.Reply(rgba, nil) } case *GetSnapshotAsCairoSurfaceResponse: var snapErr *C.GError response := obj.(*GetSnapshotAsCairoSurfaceResponse) snapResult := C.webkit_web_view_get_snapshot_finish(response.CWebView, result, &snapErr) if snapResult == nil { defer C.g_error_free(snapErr) msg := C.GoString((*C.char)(snapErr.message)) response.Reply(nil, errors.New(msg)) } else { surface := cairo.NewSurface(uintptr(unsafe.Pointer(snapResult)), false) if status := surface.Status(); status == cairo.STATUS_SUCCESS { response.Reply(surface, nil) } else { response.Reply(nil, fmt.Errorf("Cairo surface error %d", status)) } } } } }